diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 22dbc07..3acd0a7 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -1488,6 +1488,23 @@ WITH ( MODULUS numeric_literal, REM
+ vacuum_shrink_enabled (boolean)
+
+
+ Enables or disables shrinking the table when it's vacuumed.
+ The default is true. If true, VACUUM frees empty pages at the end of the table.
+ Shrinking the table requires ACCESS EXCLUSIVE lock on the table.
+ It can take non-negligible time when the shared buffer is large. Besides, ACCESS EXCLUSIVE
+ lock could lead to query cancellation on the standby server.
+ If the workload is likely to reuse the freed space soon
+ (e.g., UPDATE-heavy, or new rows will be added after deleting
+ old rows), setting this parameter to false makes sense to avoid unnecessary shrinking.
+ This parameter cannot be set for TOAST tables.
+
+
+
+
+
user_catalog_table (boolean)
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index cdf1f4a..1c4e8e2 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -89,6 +89,11 @@
* Setting parallel_workers is safe, since it acts the same as
* max_parallel_workers_per_gather which is a USERSET parameter that doesn't
* affect existing plans or queries.
+ *
+ * vacuum_shrink_enabled can be set at ShareUpdateExclusiveLock because it
+ * is only used during VACUUM, which uses a ShareUpdateExclusiveLock,
+ * so the VACUUM will not be affected by in-flight changes. Changing its
+ * value has no affect until the next VACUUM, so no need for stronger lock.
*/
static relopt_bool boolRelOpts[] =
@@ -113,6 +118,15 @@ static relopt_bool boolRelOpts[] =
},
{
{
+ "vacuum_shrink_enabled",
+ "Enables shrinking this table at vacuum",
+ RELOPT_KIND_HEAP,
+ ShareUpdateExclusiveLock
+ },
+ true
+ },
+ {
+ {
"user_catalog_table",
"Declare a table as an additional catalog table, e.g. for the purpose of logical replication",
RELOPT_KIND_HEAP,
@@ -1383,6 +1397,8 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor)},
{"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_scale_factor)},
+ {"vacuum_shrink_enabled", RELOPT_TYPE_BOOL,
+ offsetof(StdRdOptions, vacuum_shrink_enabled)},
{"user_catalog_table", RELOPT_TYPE_BOOL,
offsetof(StdRdOptions, user_catalog_table)},
{"parallel_workers", RELOPT_TYPE_INT,
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 37aa484..699d264 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -285,7 +285,9 @@ heap_vacuum_rel(Relation onerel, int options, VacuumParams *params,
/*
* Optionally truncate the relation.
*/
- if (should_attempt_truncation(vacrelstats))
+ if ((onerel->rd_options == NULL ||
+ ((StdRdOptions *) onerel->rd_options)->vacuum_shrink_enabled) &&
+ should_attempt_truncation(vacrelstats))
lazy_truncate_heap(onerel, vacrelstats);
/* Report that we are now doing final cleanup */
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 7b7a88f..a9ed116 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_shrink_enabled",
NULL
};
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 1d05465..e0cbfb6 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -251,6 +251,7 @@ typedef struct StdRdOptions
float8 vacuum_cleanup_index_scale_factor;
int toast_tuple_target; /* target for tuple toasting */
AutoVacOpts autovacuum; /* autovacuum-related options */
+ bool vacuum_shrink_enabled; /* enable table shrinking at VACUUM */
bool user_catalog_table; /* use as an additional catalog relation */
int parallel_workers; /* max number of parallel workers */
} StdRdOptions;
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
index f90c267..3451456 100644
--- a/src/test/regress/expected/reloptions.out
+++ b/src/test/regress/expected/reloptions.out
@@ -86,6 +86,40 @@ SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND
-- RESET fails if a value is specified
ALTER TABLE reloptions_test RESET (fillfactor=12);
ERROR: RESET must not include values for parameters
+-- Test vacuum_shrink_enabled option
+ALTER TABLE reloptions_test SET (vacuum_shrink_enabled=false,
+ autovacuum_enabled=false);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+--------------------------------------------------------
+ {vacuum_shrink_enabled=false,autovacuum_enabled=false}
+(1 row)
+
+INSERT INTO reloptions_test VALUES (1);
+DELETE FROM reloptions_test;
+VACUUM reloptions_test;
+SELECT pg_relation_size('reloptions_test');
+ pg_relation_size
+------------------
+ 8192
+(1 row)
+
+ALTER TABLE reloptions_test RESET (vacuum_shrink_enabled);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+----------------------------
+ {autovacuum_enabled=false}
+(1 row)
+
+INSERT INTO reloptions_test VALUES (1);
+DELETE FROM reloptions_test;
+VACUUM reloptions_test;
+SELECT pg_relation_size('reloptions_test');
+ pg_relation_size
+------------------
+ 0
+(1 row)
+
-- Test toast.* options
DROP TABLE reloptions_test;
CREATE TABLE reloptions_test (s VARCHAR)
diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sql
index 44fcd8c..8b54579 100644
--- a/src/test/regress/sql/reloptions.sql
+++ b/src/test/regress/sql/reloptions.sql
@@ -52,6 +52,22 @@ SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND
-- RESET fails if a value is specified
ALTER TABLE reloptions_test RESET (fillfactor=12);
+-- Test vacuum_shrink_enabled option
+ALTER TABLE reloptions_test SET (vacuum_shrink_enabled=false,
+ autovacuum_enabled=false);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+INSERT INTO reloptions_test VALUES (1);
+DELETE FROM reloptions_test;
+VACUUM reloptions_test;
+SELECT pg_relation_size('reloptions_test');
+
+ALTER TABLE reloptions_test RESET (vacuum_shrink_enabled);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+INSERT INTO reloptions_test VALUES (1);
+DELETE FROM reloptions_test;
+VACUUM reloptions_test;
+SELECT pg_relation_size('reloptions_test');
+
-- Test toast.* options
DROP TABLE reloptions_test;