]> granicus.if.org Git - postgresql/commitdiff
Allow VACUUM to be run with index cleanup disabled.
authorRobert Haas <rhaas@postgresql.org>
Thu, 4 Apr 2019 18:58:53 +0000 (14:58 -0400)
committerRobert Haas <rhaas@postgresql.org>
Thu, 4 Apr 2019 19:04:43 +0000 (15:04 -0400)
This commit adds a new reloption, vacuum_index_cleanup, which
controls whether index cleanup is performed for a particular
relation by default.  It also adds a new option to the VACUUM
command, INDEX_CLEANUP, which can be used to override the
reloption.  If neither the reloption nor the VACUUM option is
used, the default is true, as before.

Masahiko Sawada, reviewed and tested by Nathan Bossart, Alvaro
Herrera, Kyotaro Horiguchi, Darafei Praliaskouski, and me.
The wording of the documentation is mostly due to me.

Discussion: http://postgr.es/m/CAD21AoAt5R3DNUZSjOoXDUY=naYPUOuffVsRzuTYMz29yLzQCA@mail.gmail.com

doc/src/sgml/ref/create_table.sgml
doc/src/sgml/ref/vacuum.sgml
src/backend/access/common/reloptions.c
src/backend/access/heap/vacuumlazy.c
src/backend/commands/vacuum.c
src/backend/postmaster/autovacuum.c
src/bin/psql/tab-complete.c
src/include/commands/vacuum.h
src/include/utils/rel.h
src/test/regress/expected/vacuum.out
src/test/regress/sql/vacuum.sql

index 85c0ec1b318585db06b65202ae181bf043df57a2..1a8184e306331390fcf45336c3c65bd1c21c7d62 100644 (file)
@@ -1389,6 +1389,21 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>vacuum_index_cleanup</literal> (<type>boolean</type>)</term>
+    <listitem>
+     <para>
+      Enables or disables index cleanup when <command>VACUUM</command> is
+      run on this table.  The default value is <literal>true</literal>.
+      Disabling 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 to <xref linkend="sql-vacuum"/>, if specified, overrides
+      the value of this option.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><literal>autovacuum_vacuum_threshold</literal>, <literal>toast.autovacuum_vacuum_threshold</literal> (<type>integer</type>)</term>
     <listitem>
index 906d0c2ad7c18c46962fb8855281f8b874c8a5ac..fdd81512209f1f098eb13049dd03ee7afb736e04 100644 (file)
@@ -32,6 +32,7 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="paramet
     ANALYZE [ <replaceable class="parameter">boolean</replaceable> ]
     DISABLE_PAGE_SKIPPING [ <replaceable class="parameter">boolean</replaceable> ]
     SKIP_LOCKED [ <replaceable class="parameter">boolean</replaceable> ]
+    INDEX_CLEANUP [ <replaceable class="parameter">boolean</replaceable> ]
 
 <phrase>and <replaceable class="parameter">table_and_columns</replaceable> is:</phrase>
 
@@ -181,6 +182,28 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="paramet
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>INDEX_CLEANUP</literal></term>
+    <listitem>
+     <para>
+      Specifies that <command>VACUUM</command> should attempt to remove
+      index entries pointing to dead tuples.  This is normally the desired
+      behavior and is the default unless the
+      <literal>vacuum_index_cleanup</literal> option has been set to false
+      for the table to be vacuumed.  Setting this option to false may be
+      useful when it is necessary to make vacuum run as quickly as possible,
+      for example to avoid imminent transaction ID wraparound
+      (see <xref linkend="vacuum-for-wraparound"/>).  However, 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.  This option has no effect
+      for tables that do not have an index and is ignored if the
+      <literal>FULL</literal> is used.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><replaceable class="parameter">boolean</replaceable></term>
     <listitem>
index b58a1f7a729715d26e37e88dd8d92ea45e721bc5..e2c0de352ad80d032a002413e6b5cdbbe4c690c8 100644 (file)
@@ -138,6 +138,15 @@ static relopt_bool boolRelOpts[] =
                },
                false
        },
+       {
+               {
+                       "vacuum_index_cleanup",
+                       "Enables index vacuuming and index cleanup",
+                       RELOPT_KIND_HEAP,
+                       ShareUpdateExclusiveLock
+               },
+               true
+       },
        /* list terminator */
        {{NULL}}
 };
@@ -1388,7 +1397,9 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
                {"parallel_workers", RELOPT_TYPE_INT,
                offsetof(StdRdOptions, parallel_workers)},
                {"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
-               offsetof(StdRdOptions, vacuum_cleanup_index_scale_factor)}
+               offsetof(StdRdOptions, vacuum_cleanup_index_scale_factor)},
+               {"vacuum_index_cleanup", RELOPT_TYPE_BOOL,
+               offsetof(StdRdOptions, vacuum_index_cleanup)}
        };
 
        options = parseRelOptions(reloptions, validate, kind, &numoptions);
index 392b35ebb77ee4e1e5b785ccf883b9b963d408c0..c9d83128d5fdca1c1d6250d21977c9a86a30a7cf 100644 (file)
 
 typedef struct LVRelStats
 {
-       /* hasindex = true means two-pass strategy; false means one-pass */
-       bool            hasindex;
+       /* useindex = true means two-pass strategy; false means one-pass */
+       bool            useindex;
        /* Overall statistics about rel */
        BlockNumber old_rel_pages;      /* previous value of pg_class.relpages */
        BlockNumber rel_pages;          /* total number of pages */
@@ -125,6 +125,8 @@ typedef struct LVRelStats
        double          new_rel_tuples; /* new estimated total # of tuples */
        double          new_live_tuples;        /* new estimated total # of live tuples */
        double          new_dead_tuples;        /* new estimated total # of dead tuples */
+       double          nleft_dead_tuples;      /* # of dead tuples we left */
+       double          nleft_dead_itemids;     /* # of dead item pointers we left */
        BlockNumber pages_removed;
        double          tuples_deleted;
        BlockNumber nonempty_pages; /* actually, last nonempty page + 1 */
@@ -150,7 +152,7 @@ static BufferAccessStrategy vac_strategy;
 
 
 /* non-export function prototypes */
-static void lazy_scan_heap(Relation onerel, int options,
+static void lazy_scan_heap(Relation onerel, VacuumParams *params,
                           LVRelStats *vacrelstats, Relation *Irel, int nindexes,
                           bool aggressive);
 static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats, BlockNumber nblocks);
@@ -209,6 +211,7 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
        MultiXactId new_min_multi;
 
        Assert(params != NULL);
+       Assert(params->index_cleanup != VACOPT_TERNARY_DEFAULT);
 
        /* measure elapsed time iff autovacuum logging requires it */
        if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
@@ -275,10 +278,11 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
 
        /* Open all indexes of the relation */
        vac_open_indexes(onerel, RowExclusiveLock, &nindexes, &Irel);
-       vacrelstats->hasindex = (nindexes > 0);
+       vacrelstats->useindex = (nindexes > 0 &&
+                                                        params->index_cleanup == VACOPT_TERNARY_ENABLED);
 
        /* Do the vacuuming */
-       lazy_scan_heap(onerel, params->options, vacrelstats, Irel, nindexes, aggressive);
+       lazy_scan_heap(onerel, params, vacrelstats, Irel, nindexes, aggressive);
 
        /* Done with indexes */
        vac_close_indexes(nindexes, Irel, NoLock);
@@ -349,7 +353,7 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
                                                new_rel_pages,
                                                new_live_tuples,
                                                new_rel_allvisible,
-                                               vacrelstats->hasindex,
+                                               nindexes > 0,
                                                new_frozen_xid,
                                                new_min_multi,
                                                false);
@@ -419,6 +423,12 @@ heap_vacuum_rel(Relation onerel, VacuumParams *params,
                                                         vacrelstats->new_rel_tuples,
                                                         vacrelstats->new_dead_tuples,
                                                         OldestXmin);
+                       if (vacrelstats->nleft_dead_tuples > 0 ||
+                               vacrelstats->nleft_dead_itemids > 0)
+                               appendStringInfo(&buf,
+                                                                _("%.0f tuples and %.0f item identifiers are left as dead.\n"),
+                                                                vacrelstats->nleft_dead_tuples,
+                                                                vacrelstats->nleft_dead_itemids);
                        appendStringInfo(&buf,
                                                         _("buffer usage: %d hits, %d misses, %d dirtied\n"),
                                                         VacuumPageHit,
@@ -485,7 +495,7 @@ vacuum_log_cleanup_info(Relation rel, LVRelStats *vacrelstats)
  *             reference them have been killed.
  */
 static void
-lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
+lazy_scan_heap(Relation onerel, VacuumParams *params, LVRelStats *vacrelstats,
                           Relation *Irel, int nindexes, bool aggressive)
 {
        BlockNumber nblocks,
@@ -501,7 +511,10 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
                                live_tuples,    /* live tuples (reltuples estimate) */
                                tups_vacuumed,  /* tuples cleaned up by vacuum */
                                nkeep,                  /* dead-but-not-removable tuples */
-                               nunused;                /* unused item pointers */
+                               nunused,                /* unused item pointers */
+                               nleft_dead_tuples,              /* tuples we left as dead */
+                               nleft_dead_itemids;             /* item pointers we left as dead,
+                                                                                * includes nleft_dead_tuples. */
        IndexBulkDeleteResult **indstats;
        int                     i;
        PGRUsage        ru0;
@@ -534,6 +547,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
        empty_pages = vacuumed_pages = 0;
        next_fsm_block_to_vacuum = (BlockNumber) 0;
        num_tuples = live_tuples = tups_vacuumed = nkeep = nunused = 0;
+       nleft_dead_itemids = nleft_dead_tuples = 0;
 
        indstats = (IndexBulkDeleteResult **)
                palloc0(nindexes * sizeof(IndexBulkDeleteResult *));
@@ -599,7 +613,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
         * be replayed on any hot standby, where it can be disruptive.
         */
        next_unskippable_block = 0;
-       if ((options & VACOPT_DISABLE_PAGE_SKIPPING) == 0)
+       if ((params->options & VACOPT_DISABLE_PAGE_SKIPPING) == 0)
        {
                while (next_unskippable_block < nblocks)
                {
@@ -654,7 +668,7 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
                {
                        /* Time to advance next_unskippable_block */
                        next_unskippable_block++;
-                       if ((options & VACOPT_DISABLE_PAGE_SKIPPING) == 0)
+                       if ((params->options & VACOPT_DISABLE_PAGE_SKIPPING) == 0)
                        {
                                while (next_unskippable_block < nblocks)
                                {
@@ -1070,7 +1084,17 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
                                                HeapTupleIsHeapOnly(&tuple))
                                                nkeep += 1;
                                        else
+                                       {
                                                tupgone = true; /* we can delete the tuple */
+
+                                               /*
+                                                * Since this dead tuple will not be vacuumed and
+                                                * ignored when index cleanup is disabled we count
+                                                * count it for reporting.
+                                                */
+                                               if (params->index_cleanup == VACOPT_TERNARY_ENABLED)
+                                                       nleft_dead_tuples++;
+                                       }
                                        all_visible = false;
                                        break;
                                case HEAPTUPLE_LIVE:
@@ -1222,15 +1246,33 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
                }
 
                /*
-                * If there are no indexes then we can vacuum the page right now
-                * instead of doing a second scan.
+                * If there are no indexes we can vacuum the page right now instead of
+                * doing a second scan. Also we don't do that but forget dead tuples
+                * when index cleanup is disabled.
                 */
-               if (nindexes == 0 &&
-                       vacrelstats->num_dead_tuples > 0)
+               if (!vacrelstats->useindex && vacrelstats->num_dead_tuples > 0)
                {
-                       /* Remove tuples from heap */
-                       lazy_vacuum_page(onerel, blkno, buf, 0, vacrelstats, &vmbuffer);
-                       has_dead_tuples = false;
+                       if (nindexes == 0)
+                       {
+                               /* Remove tuples from heap if the table has no index */
+                               lazy_vacuum_page(onerel, blkno, buf, 0, vacrelstats, &vmbuffer);
+                               vacuumed_pages++;
+                               has_dead_tuples = false;
+                       }
+                       else
+                       {
+                               /*
+                                * Here, we have indexes but index cleanup is disabled. Instead of
+                                * vacuuming the dead tuples on the heap, we just forget them.
+                                *
+                                * Note that vacrelstats->dead_tuples could have tuples which
+                                * became dead after HOT-pruning but are not marked dead yet.
+                                * We do not process them because it's a very rare condition, and
+                                * the next vacuum will process them anyway.
+                                */
+                               Assert(params->index_cleanup == VACOPT_TERNARY_DISABLED);
+                               nleft_dead_itemids += vacrelstats->num_dead_tuples;
+                       }
 
                        /*
                         * Forget the now-vacuumed tuples, and press on, but be careful
@@ -1238,7 +1280,6 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
                         * valid.
                         */
                        vacrelstats->num_dead_tuples = 0;
-                       vacuumed_pages++;
 
                        /*
                         * Periodically do incremental FSM vacuuming to make newly-freed
@@ -1357,6 +1398,11 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
                        RecordPageWithFreeSpace(onerel, blkno, freespace, nblocks);
        }
 
+       /* No dead tuples should be left if index cleanup is enabled */
+       Assert((params->index_cleanup == VACOPT_TERNARY_ENABLED &&
+                       nleft_dead_tuples == 0 && nleft_dead_itemids == 0) ||
+                  params->index_cleanup == VACOPT_TERNARY_DISABLED);
+
        /* report that everything is scanned and vacuumed */
        pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_SCANNED, blkno);
 
@@ -1364,7 +1410,9 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
 
        /* save stats for use later */
        vacrelstats->tuples_deleted = tups_vacuumed;
-       vacrelstats->new_dead_tuples = nkeep;
+       vacrelstats->new_dead_tuples = nkeep + nleft_dead_tuples;
+       vacrelstats->nleft_dead_tuples = nleft_dead_tuples;
+       vacrelstats->nleft_dead_itemids = nleft_dead_itemids;
 
        /* now we can compute the new value for pg_class.reltuples */
        vacrelstats->new_live_tuples = vac_estimate_reltuples(onerel,
@@ -1433,8 +1481,11 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
                                                                 PROGRESS_VACUUM_PHASE_INDEX_CLEANUP);
 
        /* Do post-vacuum cleanup and statistics update for each index */
-       for (i = 0; i < nindexes; i++)
-               lazy_cleanup_index(Irel[i], indstats[i], vacrelstats);
+       if (vacrelstats->useindex)
+       {
+               for (i = 0; i < nindexes; i++)
+                       lazy_cleanup_index(Irel[i], indstats[i], vacrelstats);
+       }
 
        /* If no indexes, make log report that lazy_vacuum_heap would've made */
        if (vacuumed_pages)
@@ -1465,6 +1516,8 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats,
                                                                        "%u pages are entirely empty.\n",
                                                                        empty_pages),
                                         empty_pages);
+       appendStringInfo(&buf, "%.0f tuples and %.0f item identifiers are left as dead.\n",
+                                        nleft_dead_tuples, nleft_dead_itemids);
        appendStringInfo(&buf, _("%s."), pg_rusage_show(&ru0));
 
        ereport(elevel,
@@ -2110,7 +2163,7 @@ lazy_space_alloc(LVRelStats *vacrelstats, BlockNumber relblocks)
        autovacuum_work_mem != -1 ?
        autovacuum_work_mem : maintenance_work_mem;
 
-       if (vacrelstats->hasindex)
+       if (vacrelstats->useindex)
        {
                maxtuples = (vac_work_mem * 1024L) / sizeof(ItemPointerData);
                maxtuples = Min(maxtuples, INT_MAX);
index fd2e47ffc4f321190b865ef9281017a462ac7342..1a7291d94bc45590e3eb5e633b6e8e50704b97f2 100644 (file)
@@ -76,6 +76,7 @@ static void vac_truncate_clog(TransactionId frozenXID,
                                  TransactionId lastSaneFrozenXid,
                                  MultiXactId lastSaneMinMulti);
 static bool vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params);
+static VacOptTernaryValue get_vacopt_ternary_value(DefElem *def);
 
 /*
  * Primary entry point for manual VACUUM and ANALYZE commands
@@ -95,6 +96,9 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
        bool disable_page_skipping = false;
        ListCell        *lc;
 
+       /* Set default value */
+       params.index_cleanup = VACOPT_TERNARY_DEFAULT;
+
        /* Parse options list */
        foreach(lc, vacstmt->options)
        {
@@ -120,6 +124,8 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
                        full = defGetBoolean(opt);
                else if (strcmp(opt->defname, "disable_page_skipping") == 0)
                        disable_page_skipping = defGetBoolean(opt);
+               else if (strcmp(opt->defname, "index_cleanup") == 0)
+                       params.index_cleanup = get_vacopt_ternary_value(opt);
                else
                        ereport(ERROR,
                                        (errcode(ERRCODE_SYNTAX_ERROR),
@@ -1719,6 +1725,16 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params)
        onerelid = onerel->rd_lockInfo.lockRelId;
        LockRelationIdForSession(&onerelid, lmode);
 
+       /* Set index cleanup option based on reloptions if not yet */
+       if (params->index_cleanup == VACOPT_TERNARY_DEFAULT)
+       {
+               if (onerel->rd_options == NULL ||
+                       ((StdRdOptions *) onerel->rd_options)->vacuum_index_cleanup)
+                       params->index_cleanup = VACOPT_TERNARY_ENABLED;
+               else
+                       params->index_cleanup = VACOPT_TERNARY_DISABLED;
+       }
+
        /*
         * Remember the relation's TOAST relation for later, if the caller asked
         * us to process it.  In VACUUM FULL, though, the toast table is
@@ -1899,3 +1915,15 @@ vacuum_delay_point(void)
                CHECK_FOR_INTERRUPTS();
        }
 }
+
+/*
+ * A wrapper function of defGetBoolean().
+ *
+ * This function returns VACOPT_TERNARY_ENABLED and VACOPT_TERNARY_DISABLED
+ * instead of true and false.
+ */
+static VacOptTernaryValue
+get_vacopt_ternary_value(DefElem *def)
+{
+       return defGetBoolean(def) ? VACOPT_TERNARY_ENABLED : VACOPT_TERNARY_DISABLED;
+}
index fa875db8160c440d5fbd7828c119e1136aa8dd0f..0976029e7379fad40dec81098fe0aa1822e4e02f 100644 (file)
@@ -2886,6 +2886,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
                        (dovacuum ? VACOPT_VACUUM : 0) |
                        (doanalyze ? VACOPT_ANALYZE : 0) |
                        (!wraparound ? VACOPT_SKIP_LOCKED : 0);
+               tab->at_params.index_cleanup = VACOPT_TERNARY_DEFAULT;
                tab->at_params.freeze_min_age = freeze_min_age;
                tab->at_params.freeze_table_age = freeze_table_age;
                tab->at_params.multixact_freeze_min_age = multixact_freeze_min_age;
index d34bf86fc200ce592a8fcf2dfbc8e05f4c8a3bc7..22576adc51e0713bb4d39cea09c2c79a52c3b29e 100644 (file)
@@ -1039,6 +1039,7 @@ static const char *const table_storage_parameters[] = {
        "toast.log_autovacuum_min_duration",
        "toast_tuple_target",
        "user_catalog_table",
+       "vacuum_index_cleanup",
        NULL
 };
 
@@ -3443,8 +3444,9 @@ psql_completion(const char *text, int start, int end)
                 */
                if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
                        COMPLETE_WITH("FULL", "FREEZE", "ANALYZE", "VERBOSE",
-                                                 "DISABLE_PAGE_SKIPPING", "SKIP_LOCKED");
-               else if (TailMatches("FULL|FREEZE|ANALYZE|VERBOSE|DISABLE_PAGE_SKIPPING|SKIP_LOCKED"))
+                                                 "DISABLE_PAGE_SKIPPING", "SKIP_LOCKED",
+                                                 "INDEX_CLEANUP");
+               else if (TailMatches("FULL|FREEZE|ANALYZE|VERBOSE|DISABLE_PAGE_SKIPPING|SKIP_LOCKED|INDEX_CLEANUP"))
                        COMPLETE_WITH("ON", "OFF");
        }
        else if (HeadMatches("VACUUM") && TailMatches("("))
index 77086f3e9130ef41624ac34208df7ef94598b25f..9cc6e0d02352b0deb991b99c2f0fff463d7ea5a8 100644 (file)
@@ -148,6 +148,19 @@ typedef enum VacuumOption
        VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7   /* don't skip any pages */
 } VacuumOption;
 
+/*
+ * A ternary value used by vacuum parameters.
+ *
+ * DEFAULT value is used to determine the value based on other
+ * configurations, e.g. reloptions.
+ */
+typedef enum VacOptTernaryValue
+{
+       VACOPT_TERNARY_DEFAULT = 0,
+       VACOPT_TERNARY_DISABLED,
+       VACOPT_TERNARY_ENABLED,
+} VacOptTernaryValue;
+
 /*
  * Parameters customizing behavior of VACUUM and ANALYZE.
  *
@@ -167,6 +180,8 @@ typedef struct VacuumParams
        int                     log_min_duration;       /* minimum execution threshold in ms at
                                                                         * which  verbose logs are activated, -1
                                                                         * to use default */
+       VacOptTernaryValue index_cleanup;       /* Do index vacuum and cleanup,
+                                                                               * default value depends on reloptions */
 } VacuumParams;
 
 /* GUC parameters */
index 54028515a7c8e7783c3cf34a362ce42bb4eb9c28..89a7fbf73a5098de8e9585ad1ae4f30aec47a510 100644 (file)
@@ -266,6 +266,7 @@ typedef struct StdRdOptions
        AutoVacOpts autovacuum;         /* autovacuum-related options */
        bool            user_catalog_table; /* use as an additional catalog relation */
        int                     parallel_workers;       /* max number of parallel workers */
+       bool            vacuum_index_cleanup;   /* enables index vacuuming and cleanup */
 } StdRdOptions;
 
 #define HEAP_MIN_FILLFACTOR                    10
index 07d0703115845ce4bf4229541c28b4d2e677b9ed..6ba7cd726bc9354778b4de3ae9a5365deafbee44 100644 (file)
@@ -80,6 +80,14 @@ CONTEXT:  SQL function "do_analyze" statement 1
 SQL function "wrap_do_analyze" statement 1
 VACUUM FULL vactst;
 VACUUM (DISABLE_PAGE_SKIPPING) vaccluster;
+-- INDEX_CLEANUP option
+CREATE TABLE no_index_cleanup (i INT PRIMARY KEY) WITH (vacuum_index_cleanup = false);
+VACUUM (INDEX_CLEANUP FALSE) vaccluster;
+VACUUM (INDEX_CLEANUP FALSE) vactst; -- index cleanup option is ignored if no indexes
+VACUUM (INDEX_CLEANUP FALSE, FREEZE TRUE) vaccluster;
+-- index cleanup option is ignored if VACUUM FULL
+VACUUM (INDEX_CLEANUP TRUE, FULL TRUE) no_index_cleanup;
+VACUUM (FULL TRUE) no_index_cleanup;
 -- partitioned table
 CREATE TABLE vacparted (a int, b char) PARTITION BY LIST (a);
 CREATE TABLE vacparted1 PARTITION OF vacparted FOR VALUES IN (1);
@@ -136,6 +144,7 @@ ANALYZE (SKIP_LOCKED) vactst;
 DROP TABLE vaccluster;
 DROP TABLE vactst;
 DROP TABLE vacparted;
+DROP TABLE no_index_cleanup;
 -- relation ownership, WARNING logs generated as all are skipped.
 CREATE TABLE vacowned (a int);
 CREATE TABLE vacowned_parted (a int) PARTITION BY LIST (a);
index 81f382267964bbc10f1e1d96195a400ab3524547..57e0f354ddaf73f4720cb60ce7c6e5a9a35d90d3 100644 (file)
@@ -62,6 +62,15 @@ VACUUM FULL vactst;
 
 VACUUM (DISABLE_PAGE_SKIPPING) vaccluster;
 
+-- INDEX_CLEANUP option
+CREATE TABLE no_index_cleanup (i INT PRIMARY KEY) WITH (vacuum_index_cleanup = false);
+VACUUM (INDEX_CLEANUP FALSE) vaccluster;
+VACUUM (INDEX_CLEANUP FALSE) vactst; -- index cleanup option is ignored if no indexes
+VACUUM (INDEX_CLEANUP FALSE, FREEZE TRUE) vaccluster;
+-- index cleanup option is ignored if VACUUM FULL
+VACUUM (INDEX_CLEANUP TRUE, FULL TRUE) no_index_cleanup;
+VACUUM (FULL TRUE) no_index_cleanup;
+
 -- partitioned table
 CREATE TABLE vacparted (a int, b char) PARTITION BY LIST (a);
 CREATE TABLE vacparted1 PARTITION OF vacparted FOR VALUES IN (1);
@@ -107,6 +116,7 @@ ANALYZE (SKIP_LOCKED) vactst;
 DROP TABLE vaccluster;
 DROP TABLE vactst;
 DROP TABLE vacparted;
+DROP TABLE no_index_cleanup;
 
 -- relation ownership, WARNING logs generated as all are skipped.
 CREATE TABLE vacowned (a int);