On Thu, Mar 7, 2019 at 3:55 AM Robert Haas <robertmh...@gmail.com> wrote: > > On Tue, Mar 5, 2019 at 11:29 PM Masahiko Sawada <sawada.m...@gmail.com> wrote: > > Attached updated patch incorporated all of comments. Also I've added > > new reloption vacuum_index_cleanup as per discussion on the "reloption > > to prevent VACUUM from truncating empty pages at the end of relation" > > thread. Autovacuums also can skip index cleanup when the reloption is > > set to false. Since the setting this to false might lead some problems > > I've made autovacuums report the number of dead tuples and dead > > itemids we left. > > It seems to me that the disable_index_cleanup should be renamed > index_cleanup and the default should be changed to true, for > consistency with the reloption (and, perhaps, other patches).
Hmm, the patch already has new reloption vacuum_index_cleanup and default value is true but you meant I should change its name to index_cleanup? > > - num_tuples = live_tuples = tups_vacuumed = nkeep = nunused = 0; > + num_tuples = live_tuples = tups_vacuumed = nkeep = nunused = > + nleft_dead_itemids = nleft_dead_tuples = 0; > > I would suggest leaving the existing line alone (and not adding an > extra space to it as the patch does now) and just adding a second > initialization on the next line as a separate statement. a = b = c = d > = e = 0 isn't such great coding style that we should stick to it > rigorously even when it ends up having to be broken across lines. Fixed. > > + /* Index vacuum must be enabled in two-pass vacuum */ > + Assert(!skip_index_vacuum); > > I am a big believer in naming consistency. Please, let's use the same > name everywhere! If it's going to be index_cleanup, then call the > reloption vacuum_index_cleanup, and call the option index_cleanup, and > call the variable index_cleanup. Picking a different subset of > cleanup, index, vacuum, skip, and disable for each new name makes it > harder to understand. Fixed. > > - * If there are no indexes then we can vacuum the page right now > - * instead of doing a second scan. > + * If there are no indexes or index vacuum is disabled we can > + * vacuum the page right now instead of doing a second scan. > > This comment is wrong. That wouldn't be safe. And that's probably > why it's not what the code does. Fixed. > > - /* If no indexes, make log report that lazy_vacuum_heap would've made */ > + /* > + * If no index or index vacuum is disabled, make log report that > + * lazy_vacuum_heap would've make. > + */ > if (vacuumed_pages) > > Hmm, does this really do what the comment claims? It looks to me like > we only increment vacuumed_pages when we call lazy_vacuum_page(), and > we (correctly) don't do that when index cleanup is disabled, but then > here this claims that if (vacuumed_pages) will be true in that case. You're right, vacuumed_pages never be > 0 in disable_index_cleanup case. Fixed. > > I wonder if it would be cleaner to rename vacrelstate->hasindex to > 'useindex' and set it to false if there are no indexes or index > cleanup is disabled. But that might actually be worse, not sure. > I tried the changes and it seems good idea to me. Fixed. Attached the updated version patches. Regards, -- Masahiko Sawada NIPPON TELEGRAPH AND TELEPHONE CORPORATION NTT Open Source Software Center
From 8ab648be35fe4fd864fabd17adf72fb476a3367c Mon Sep 17 00:00:00 2001 From: Masahiko Sawada <sawada.m...@gmail.com> Date: Thu, 7 Mar 2019 09:45:11 +0900 Subject: [PATCH v9 1/2] Add DISABLE_INDEX_CLEANUP option to VACUUM command With this option, VACUUM does HOT-pruning for live tuples but doesn't remove dead tuples completely and disables index vacuum. vacrelstats->dead_tuples could have tuples that became dead after checked at a HOT-pruning time, which are not marked as dead. Per discussion on pgsql-hackers We normally records and remove them but with this option we don't process and leave for the next vacuum for simplifing the code. That's okay because it's very rare condition and those tuples will be processed by the next vacuum. --- doc/src/sgml/ref/create_table.sgml | 16 ++++++++ doc/src/sgml/ref/vacuum.sgml | 27 ++++++++++++ src/backend/access/common/reloptions.c | 13 +++++- src/backend/access/heap/vacuumlazy.c | 75 ++++++++++++++++++++++++++-------- src/backend/commands/vacuum.c | 15 ++++++- src/backend/parser/gram.y | 2 + src/bin/psql/tab-complete.c | 4 +- src/include/nodes/parsenodes.h | 3 +- src/include/utils/rel.h | 1 + src/test/regress/expected/vacuum.out | 3 ++ src/test/regress/sql/vacuum.sql | 3 ++ 11 files changed, 142 insertions(+), 20 deletions(-) diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml index 22dbc07..cd9cea9 100644 --- a/doc/src/sgml/ref/create_table.sgml +++ b/doc/src/sgml/ref/create_table.sgml @@ -1345,6 +1345,22 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM </varlistentry> <varlistentry> + <term><literal>vacuum_index_cleanup</literal> (<type>boolean</type>)</term> + <listitem> + <para> + Per table setting to use <literal>DISABLE_INDEX_CLEANUP</literal> option + of <command>VACUUM</command> command. The default value is true. If false, + autovacuum daemon and <command>VACUUM</command> never perform index vacuuming + and index cleanup, that is, always set <literal>DISABLE_INDEX_CLEANUP</literal> + option. Note that out of disk space due to index bloat. Setting this + parameter to false makes sense to avoid scanning large indexes when the + table has a few dead tuples. See <xref linkend="sql-vacuum"/> for more + details on <literal>DISABLE_INDEX_CLEANUP</literal> option. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><literal>autovacuum_vacuum_threshold</literal>, <literal>toast.autovacuum_vacuum_threshold</literal> (<type>integer</type>)</term> <listitem> <para> diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml index fd911f5..3162793 100644 --- a/doc/src/sgml/ref/vacuum.sgml +++ b/doc/src/sgml/ref/vacuum.sgml @@ -31,6 +31,7 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="paramet VERBOSE ANALYZE DISABLE_PAGE_SKIPPING + DISABLE_INDEX_CLEANUP SKIP_LOCKED <phrase>and <replaceable class="parameter">table_and_columns</replaceable> is:</phrase> @@ -162,6 +163,25 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="paramet </varlistentry> <varlistentry> + <term><literal>DISABLE_INDEX_CLEANUP</literal></term> + <listitem> + <para> + <command>VACUUM</command> removes dead tuples and prunes HOT-updated + tuples chain for live tuples on table. If the table has any dead tuple + it removes them from both the table and its indexes and marks the + corresponding item identifers as available for re-use. With this option + <command>VACUUM</command> still removes dead tuples from the table, but + it does not process any indexes, and the item identifers are marked as + dead instead of available. This is suitable for avoiding transaction + ID wraparound (see <xref linkend="vacuum-for-wraparound"/>) but not + sufficient for avoiding index bloat. This option is ignored if the + table does not have index. This cannot be used in conjunction with + <literal>FULL</literal> option. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><literal>SKIP_LOCKED</literal></term> <listitem> <para> @@ -261,6 +281,13 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="paramet </para> <para> + Setting a values for <literal>vacuum_index_cleanup</literal> via + <xref linkend="sql-altertable"/> also enables and disables index cleanup. + The <literal>DISABLE_INDEX_CLEANUP</literal> options to + <command>VACUUM</command> takes precedence over this option. + </para> + + <para> <command>VACUUM</command> causes a substantial increase in I/O traffic, which might cause poor performance for other active sessions. Therefore, it is sometimes advisable to use the cost-based vacuum delay feature. diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c index cdf1f4a..2cb423d 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -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); diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c index 9416c31..cd5b975 100644 --- a/src/backend/access/heap/vacuumlazy.c +++ b/src/backend/access/heap/vacuumlazy.c @@ -112,8 +112,8 @@ 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 */ @@ -258,7 +260,8 @@ heap_vacuum_rel(Relation onerel, int options, VacuumParams *params, /* Open all indexes of the relation */ vac_open_indexes(onerel, RowExclusiveLock, &nindexes, &Irel); - vacrelstats->hasindex = (nindexes > 0); + vacrelstats->useindex = (nindexes > 0 && + (options & VACOPT_DISABLE_INDEX_CLEANUP) == 0); /* Do the vacuuming */ lazy_scan_heap(onerel, options, vacrelstats, Irel, nindexes, aggressive); @@ -332,7 +335,7 @@ heap_vacuum_rel(Relation onerel, int options, VacuumParams *params, new_rel_pages, new_live_tuples, new_rel_allvisible, - vacrelstats->hasindex, + nindexes > 0, new_frozen_xid, new_min_multi, false); @@ -403,6 +406,9 @@ heap_vacuum_rel(Relation onerel, int options, VacuumParams *params, vacrelstats->new_rel_tuples, vacrelstats->new_dead_tuples, OldestXmin); + 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 +491,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; @@ -518,6 +527,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 *)); @@ -1054,7 +1064,16 @@ lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, HeapTupleIsHeapOnly(&tuple)) nkeep += 1; else + { tupgone = true; /* we can delete the tuple */ + + /* + * Since the dead tuples will be not be vacuumed + * and ignored when index cleanup is disabled we + * count them for reporting. + */ + nleft_dead_tuples++; + } all_visible = false; break; case HEAPTUPLE_LIVE: @@ -1207,14 +1226,32 @@ 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. + * instead of doing a second scan. Also we don't do that and 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((options & VACOPT_DISABLE_INDEX_CLEANUP) != 0); + nleft_dead_itemids += vacrelstats->num_dead_tuples; + } /* * Forget the now-vacuumed tuples, and press on, but be careful @@ -1222,7 +1259,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 @@ -1348,7 +1384,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, @@ -1417,8 +1455,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) @@ -1449,6 +1490,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, @@ -2092,7 +2135,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); diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index e91df21..a0f2622 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -203,7 +203,8 @@ vacuum(int options, List *relations, VacuumParams *params, stmttype))); /* - * Sanity check DISABLE_PAGE_SKIPPING option. + * Sanity check DISABLE_PAGE_SKIPPING option and DISABLE_INDEX_CLEANUP + * option. */ if ((options & VACOPT_FULL) != 0 && (options & VACOPT_DISABLE_PAGE_SKIPPING) != 0) @@ -211,6 +212,11 @@ vacuum(int options, List *relations, VacuumParams *params, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("VACUUM option DISABLE_PAGE_SKIPPING cannot be used with FULL"))); + if ((options & VACOPT_FULL) != 0 && + (options & VACOPT_DISABLE_INDEX_CLEANUP) != 0) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("VACUUM option DISABLE_INDEX_CLEANUP 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. @@ -1672,6 +1678,13 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params) LockRelationIdForSession(&onerelid, lmode); /* + * Disables index cleanup based on reloptions. + */ + if (onerel->rd_options && + !((StdRdOptions *) onerel->rd_options)->vacuum_index_cleanup) + options |= VACOPT_DISABLE_INDEX_CLEANUP; + + /* * Remember the relation's TOAST relation for later, if the caller asked * us to process it. In VACUUM FULL, though, the toast table is * automatically rebuilt by cluster_rel so we shouldn't recurse to it. diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 753af60..91f8081 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -10495,6 +10495,8 @@ vacuum_option_elem: { if (strcmp($1, "disable_page_skipping") == 0) $$ = VACOPT_DISABLE_PAGE_SKIPPING; + else if (strcmp($1, "disable_index_cleanup") == 0) + $$ = VACOPT_DISABLE_INDEX_CLEANUP; else if (strcmp($1, "skip_locked") == 0) $$ = VACOPT_SKIP_LOCKED; else diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 10ae21c..0cd03ad 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -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 }; @@ -3429,7 +3430,8 @@ 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"); + "DISABLE_PAGE_SKIPPING", "SKIP_LOCKED", + "DISABLE_INDEX_CLEANUP"); } else if (HeadMatches("VACUUM") && TailMatches("(")) /* "VACUUM (" should be caught above, so assume we want columns */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index fe35783..2e75906 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3164,7 +3164,8 @@ typedef enum VacuumOption VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */ VACOPT_SKIP_LOCKED = 1 << 5, /* skip if cannot get lock */ VACOPT_SKIPTOAST = 1 << 6, /* don't process the TOAST table, if any */ - VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don't skip any pages */ + VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7, /* don't skip any pages */ + VACOPT_DISABLE_INDEX_CLEANUP = 1 << 8 /* disable index vacuuming and cleanup */ } VacuumOption; /* diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 9d805ca..1a19e4a 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -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; /* do index vacuuming and cleanup */ } StdRdOptions; #define HEAP_MIN_FILLFACTOR 10 diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out index fa9d663..cffce59 100644 --- a/src/test/regress/expected/vacuum.out +++ b/src/test/regress/expected/vacuum.out @@ -80,6 +80,9 @@ CONTEXT: SQL function "do_analyze" statement 1 SQL function "wrap_do_analyze" statement 1 VACUUM FULL vactst; VACUUM (DISABLE_PAGE_SKIPPING) vaccluster; +VACUUM (DISABLE_INDEX_CLEANUP) vaccluster; +VACUUM (DISABLE_INDEX_CLEANUP) vactst; -- DISABLE_INDEX_CLEANUP is ignored +VACUUM (DISABLE_INDEX_CLEANUP, FREEZE) vaccluster; -- partitioned table CREATE TABLE vacparted (a int, b char) PARTITION BY LIST (a); CREATE TABLE vacparted1 PARTITION OF vacparted FOR VALUES IN (1); diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql index 9defa0d..9c4bdb7 100644 --- a/src/test/regress/sql/vacuum.sql +++ b/src/test/regress/sql/vacuum.sql @@ -61,6 +61,9 @@ VACUUM FULL vaccluster; VACUUM FULL vactst; VACUUM (DISABLE_PAGE_SKIPPING) vaccluster; +VACUUM (DISABLE_INDEX_CLEANUP) vaccluster; +VACUUM (DISABLE_INDEX_CLEANUP) vactst; -- DISABLE_INDEX_CLEANUP is ignored +VACUUM (DISABLE_INDEX_CLEANUP, FREEZE) vaccluster; -- partitioned table CREATE TABLE vacparted (a int, b char) PARTITION BY LIST (a); -- 1.8.3.1
From d25bbb5dce2d408b942ad4280cb54dc382f8757a Mon Sep 17 00:00:00 2001 From: Masahiko Sawada <sawada.m...@gmail.com> Date: Fri, 1 Feb 2019 16:02:50 +0100 Subject: [PATCH v9 2/2] Add --diable-index-cleanup option to vacuumdb. --- doc/src/sgml/ref/vacuumdb.sgml | 15 +++++++++++++++ src/bin/scripts/t/100_vacuumdb.pl | 9 ++++++++- src/bin/scripts/vacuumdb.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/doc/src/sgml/ref/vacuumdb.sgml b/doc/src/sgml/ref/vacuumdb.sgml index 41c7f3d..02d6e46 100644 --- a/doc/src/sgml/ref/vacuumdb.sgml +++ b/doc/src/sgml/ref/vacuumdb.sgml @@ -118,6 +118,21 @@ PostgreSQL documentation </varlistentry> <varlistentry> + <term><option>--disable-index-cleanup</option></term> + <listitem> + <para> + Disable index vacuuming and index cleanup. + </para> + <note> + <para> + This option is only available for servers running + <productname>PostgreSQL</productname> 12 and later. + </para> + </note> + </listitem> + </varlistentry> + + <varlistentry> <term><option>-e</option></term> <term><option>--echo</option></term> <listitem> diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl index 7f3a9b1..d227225 100644 --- a/src/bin/scripts/t/100_vacuumdb.pl +++ b/src/bin/scripts/t/100_vacuumdb.pl @@ -3,7 +3,7 @@ use warnings; use PostgresNode; use TestLib; -use Test::More tests => 44; +use Test::More tests => 47; program_help_ok('vacuumdb'); program_version_ok('vacuumdb'); @@ -38,6 +38,10 @@ $node->issues_sql_like( qr/statement: VACUUM \(DISABLE_PAGE_SKIPPING\).*;/, 'vacuumdb --disable-page-skipping'); $node->issues_sql_like( + [ 'vacuumdb', '--disable-index-cleanup', 'postgres' ], + qr/statement: VACUUM \(DISABLE_INDEX_CLEANUP\).*;/, + 'vacuumdb --disable-index-cleanup'); +$node->issues_sql_like( [ 'vacuumdb', '--skip-locked', 'postgres' ], qr/statement: VACUUM \(SKIP_LOCKED\).*;/, 'vacuumdb --skip-locked'); @@ -48,6 +52,9 @@ $node->issues_sql_like( $node->command_fails( [ 'vacuumdb', '--analyze-only', '--disable-page-skipping', 'postgres' ], '--analyze-only and --disable-page-skipping specified together'); +$node->command_fails( + [ 'vacuumdb', '--analyze-only', '--disable-index-cleanup', 'postgres' ], + '--analyze-only and --disable-index-cleanup specified together'); $node->command_ok([qw(vacuumdb -Z --table=pg_am dbname=template1)], 'vacuumdb with connection string'); diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c index 5ac41ea..4705217 100644 --- a/src/bin/scripts/vacuumdb.c +++ b/src/bin/scripts/vacuumdb.c @@ -42,6 +42,7 @@ typedef struct vacuumingOptions bool full; bool freeze; bool disable_page_skipping; + bool disable_index_cleanup; bool skip_locked; int min_xid_age; int min_mxid_age; @@ -117,6 +118,7 @@ main(int argc, char *argv[]) {"skip-locked", no_argument, NULL, 5}, {"min-xid-age", required_argument, NULL, 6}, {"min-mxid-age", required_argument, NULL, 7}, + {"disable-index-cleanup", no_argument, NULL, 8}, {NULL, 0, NULL, 0} }; @@ -244,6 +246,11 @@ main(int argc, char *argv[]) exit(1); } break; + case 8: + { + vacopts.disable_index_cleanup = true; + break; + } default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); @@ -288,6 +295,12 @@ main(int argc, char *argv[]) progname, "disable-page-skipping"); exit(1); } + if (vacopts.disable_index_cleanup) + { + fprintf(stderr, _("%s: cannot use the \"%s\" option when performing only analyze\n"), + progname, "disable-index-cleanup"); + exit(1); + } /* allow 'and_analyze' with 'analyze_only' */ } @@ -418,6 +431,14 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts, exit(1); } + if (vacopts->disable_index_cleanup && PQserverVersion(conn) < 120000) + { + PQfinish(conn); + fprintf(stderr, _("%s: cannot use the \"%s\" option on server versions older than PostgreSQL 12\n"), + progname, "disable-index-cleanup"); + exit(1); + } + if (vacopts->skip_locked && PQserverVersion(conn) < 120000) { PQfinish(conn); @@ -868,6 +889,13 @@ prepare_vacuum_command(PQExpBuffer sql, int serverVersion, appendPQExpBuffer(sql, "%sDISABLE_PAGE_SKIPPING", sep); sep = comma; } + if (vacopts->disable_index_cleanup) + { + /* DISABLE_INDEX_CLEANUP is supported since 12 */ + Assert(serverVersion >= 120000); + appendPQExpBuffer(sql, "%sDISABLE_INDEX_CLEANUP", sep); + sep = comma; + } if (vacopts->skip_locked) { /* SKIP_LOCKED is supported since v12 */ @@ -1221,6 +1249,7 @@ help(const char *progname) printf(_(" -a, --all vacuum all databases\n")); printf(_(" -d, --dbname=DBNAME database to vacuum\n")); printf(_(" --disable-page-skipping disable all page-skipping behavior\n")); + printf(_(" --disable-index-cleanup disable index vacuuming and index cleanup\n")); printf(_(" -e, --echo show the commands being sent to the server\n")); printf(_(" -f, --full do full vacuuming\n")); printf(_(" -F, --freeze freeze row transaction information\n")); -- 1.8.3.1