Disabling index cleanup can speed up VACUUM very
significantly, but may also lead to severely bloated indexes if table
modifications are frequent. The INDEX_CLEANUP
- parameter to , if specified, overrides
+ parameter of , if specified, overrides
the value of this option.
autovacuum do the truncation and the disk space for
the truncated pages is returned to the operating system.
Note that the truncation requires ACCESS EXCLUSIVE
- lock on the table.
+ lock on the table. The TRUNCATE parameter
+ of , if specified, overrides the value
+ of this option.
DISABLE_PAGE_SKIPPING [ boolean ]
SKIP_LOCKED [ boolean ]
INDEX_CLEANUP [ boolean ]
+ TRUNCATE [ boolean ]
and table_and_columns is:
+
+ TRUNCATE
+
+ Specifies that VACUUM should attempt to
+ truncate off any empty pages at the end of the table and allow
+ the disk space for the truncated pages to be returned to
+ the operating system. This is normally the desired behavior
+ and is the default unless the vacuum_truncate
+ option has been set to false for the table to be vacuumed.
+ Setting this option to false may be useful to avoid
+ ACCESS EXCLUSIVE lock on the table that
+ the truncation requires. This option is ignored if the
+ FULL option is used.
+
+
+
+
boolean
LVRelStats *vacrelstats);
static int lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
int tupindex, LVRelStats *vacrelstats, Buffer *vmbuffer);
-static bool should_attempt_truncation(Relation rel, LVRelStats *vacrelstats);
+static bool should_attempt_truncation(VacuumParams *params,
+ LVRelStats *vacrelstats);
static void lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats);
static BlockNumber count_nondeletable_pages(Relation onerel,
LVRelStats *vacrelstats);
Assert(params != NULL);
Assert(params->index_cleanup != VACOPT_TERNARY_DEFAULT);
+ Assert(params->truncate != VACOPT_TERNARY_DEFAULT);
/* not every AM requires these to be valid, but heap does */
Assert(TransactionIdIsNormal(onerel->rd_rel->relfrozenxid));
/*
* Optionally truncate the relation.
*/
- if (should_attempt_truncation(onerel, vacrelstats))
+ if (should_attempt_truncation(params, vacrelstats))
lazy_truncate_heap(onerel, vacrelstats);
/* Report that we are now doing final cleanup */
/* see note above about forcing scanning of last page */
#define FORCE_CHECK_PAGE() \
- (blkno == nblocks - 1 && should_attempt_truncation(onerel, vacrelstats))
+ (blkno == nblocks - 1 && should_attempt_truncation(params, vacrelstats))
pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_SCANNED, blkno);
* careful to depend only on fields that lazy_scan_heap updates on-the-fly.
*/
static bool
-should_attempt_truncation(Relation rel, LVRelStats *vacrelstats)
+should_attempt_truncation(VacuumParams *params, LVRelStats *vacrelstats)
{
BlockNumber possibly_freeable;
- if (rel->rd_options != NULL &&
- ((StdRdOptions *) rel->rd_options)->vacuum_truncate == false)
+ if (params->truncate == VACOPT_TERNARY_DISABLED)
return false;
possibly_freeable = vacrelstats->rel_pages - vacrelstats->nonempty_pages;
/* Set default value */
params.index_cleanup = VACOPT_TERNARY_DEFAULT;
+ params.truncate = VACOPT_TERNARY_DEFAULT;
/* Parse options list */
foreach(lc, vacstmt->options)
disable_page_skipping = defGetBoolean(opt);
else if (strcmp(opt->defname, "index_cleanup") == 0)
params.index_cleanup = get_vacopt_ternary_value(opt);
+ else if (strcmp(opt->defname, "truncate") == 0)
+ params.truncate = get_vacopt_ternary_value(opt);
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
params->index_cleanup = VACOPT_TERNARY_DISABLED;
}
+ /* Set truncate option based on reloptions if not yet */
+ if (params->truncate == VACOPT_TERNARY_DEFAULT)
+ {
+ if (onerel->rd_options == NULL ||
+ ((StdRdOptions *) onerel->rd_options)->vacuum_truncate)
+ params->truncate = VACOPT_TERNARY_ENABLED;
+ else
+ params->truncate = 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
(doanalyze ? VACOPT_ANALYZE : 0) |
(!wraparound ? VACOPT_SKIP_LOCKED : 0);
tab->at_params.index_cleanup = VACOPT_TERNARY_DEFAULT;
+ tab->at_params.truncate = 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;
if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
COMPLETE_WITH("FULL", "FREEZE", "ANALYZE", "VERBOSE",
"DISABLE_PAGE_SKIPPING", "SKIP_LOCKED",
- "INDEX_CLEANUP");
- else if (TailMatches("FULL|FREEZE|ANALYZE|VERBOSE|DISABLE_PAGE_SKIPPING|SKIP_LOCKED|INDEX_CLEANUP"))
+ "INDEX_CLEANUP", "TRUNCATE");
+ else if (TailMatches("FULL|FREEZE|ANALYZE|VERBOSE|DISABLE_PAGE_SKIPPING|SKIP_LOCKED|INDEX_CLEANUP|TRUNCATE"))
COMPLETE_WITH("ON", "OFF");
}
else if (HeadMatches("VACUUM") && TailMatches("("))
* to use default */
VacOptTernaryValue index_cleanup; /* Do index vacuum and cleanup,
* default value depends on reloptions */
+ VacOptTernaryValue truncate; /* Truncate empty pages at the end,
+ * default value depends on reloptions */
} VacuumParams;
/* GUC parameters */
-- index cleanup option is ignored if VACUUM FULL
VACUUM (INDEX_CLEANUP TRUE, FULL TRUE) no_index_cleanup;
VACUUM (FULL TRUE) no_index_cleanup;
+-- TRUNCATE option
+CREATE TABLE vac_truncate_test(i INT NOT NULL, j text)
+ WITH (vacuum_truncate=true, autovacuum_enabled=false);
+INSERT INTO vac_truncate_test VALUES (1, NULL), (NULL, NULL);
+ERROR: null value in column "i" violates not-null constraint
+DETAIL: Failing row contains (null, null).
+VACUUM (TRUNCATE FALSE) vac_truncate_test;
+SELECT pg_relation_size('vac_truncate_test') > 0;
+ ?column?
+----------
+ t
+(1 row)
+
+VACUUM vac_truncate_test;
+SELECT pg_relation_size('vac_truncate_test') = 0;
+ ?column?
+----------
+ t
+(1 row)
+
+VACUUM (TRUNCATE FALSE, FULL TRUE) vac_truncate_test;
+DROP TABLE vac_truncate_test;
-- partitioned table
CREATE TABLE vacparted (a int, b char) PARTITION BY LIST (a);
CREATE TABLE vacparted1 PARTITION OF vacparted FOR VALUES IN (1);
VACUUM (INDEX_CLEANUP TRUE, FULL TRUE) no_index_cleanup;
VACUUM (FULL TRUE) no_index_cleanup;
+-- TRUNCATE option
+CREATE TABLE vac_truncate_test(i INT NOT NULL, j text)
+ WITH (vacuum_truncate=true, autovacuum_enabled=false);
+INSERT INTO vac_truncate_test VALUES (1, NULL), (NULL, NULL);
+VACUUM (TRUNCATE FALSE) vac_truncate_test;
+SELECT pg_relation_size('vac_truncate_test') > 0;
+VACUUM vac_truncate_test;
+SELECT pg_relation_size('vac_truncate_test') = 0;
+VACUUM (TRUNCATE FALSE, FULL TRUE) vac_truncate_test;
+DROP TABLE vac_truncate_test;
+
-- partitioned table
CREATE TABLE vacparted (a int, b char) PARTITION BY LIST (a);
CREATE TABLE vacparted1 PARTITION OF vacparted FOR VALUES IN (1);