postgres.git / summary / log / commit / refs
commit 63979e5bea14c533dadc8f44cee0ec7a2899ccf5
Author: Nathan Bossart <nathan@postgresql.org>
Date: Fri Aug 21 16:50:39 2026 +0000
Ensure TOAST storage parameters have unsettable defaults.
A follow-up commit will teach autovacuum and manual VACUUM to fill
in a TOAST table's unset storage parameters from its main table's.
To be able to determine whether a parameter is set, it must default
to a value that users cannot set. Of all the parameters that can
be set on TOAST tables, log_autovacuum_min_duration is the only one
that violates this rule. This commit changes its default to -2 so
that we can tell whether it is set. This shouldn't produce any
user-visible behavior changes.
While at it, add assertions to ensure all storage parameters that
can be set on TOAST tables continue to follow this rule.
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Sami Imseih <samimseih@gmail.com>
Reviewed-by: Greg Burd <greg@burd.me>
Tested-by: solai v <solai.cdac@gmail.com>
Discussion: https://postgr.es/m/aFRxC1W_kZU9OjJ9%40nathan
src/backend/access/common/reloptions.c | 76 +++++++++++++++++++++++++++++++++-
src/backend/postmaster/autovacuum.c | 2 +-
2 files changed, 76 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index fab8294e732..7ad9a7b9a3b 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -348,7 +348,7 @@ static relopt_int intRelOpts[] =
RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
ShareUpdateExclusiveLock
},
- -1, -1, INT_MAX
+ -2, -1, INT_MAX
},
{
{
@@ -613,6 +613,76 @@ static void parse_one_reloption(relopt_value *option, char *text_str,
((option).isset ? strlen((option).string_val) : \
((relopt_string *) (option).gen)->default_len)
+#ifdef USE_ASSERT_CHECKING
+/*
+ * Verify that every option a TOAST table accepts defaults to a value the user
+ * cannot set. That way, we can tell whether a reloption is set on the TOAST
+ * table or if we should pull the value from its main table.
+ */
+static void
+assert_toast_defaults_unsettable(void)
+{
+ for (int i = 0; relOpts[i]; i++)
+ {
+ relopt_gen *gen = relOpts[i];
+
+ if ((gen->kinds & RELOPT_KIND_TOAST) == 0)
+ continue;
+
+ /*
+ * A TOAST table's value is filled in from its main table's at the
+ * same offset in the same struct, so the option must be settable on a
+ * heap too.
+ */
+ Assert((gen->kinds & RELOPT_KIND_HEAP) != 0);
+
+ switch (gen->type)
+ {
+ case RELOPT_TYPE_TERNARY:
+
+ /*
+ * Ternaries carry no default, and parse_one_reloption() can
+ * only produce true or false, so PG_TERNARY_UNSET is already
+ * beyond a user's reach.
+ */
+ break;
+
+ case RELOPT_TYPE_INT:
+ {
+ relopt_int *optint = (relopt_int *) gen;
+
+ Assert(optint->default_val < optint->min ||
+ optint->default_val > optint->max);
+ break;
+ }
+
+ case RELOPT_TYPE_REAL:
+ {
+ relopt_real *optreal = (relopt_real *) gen;
+
+ Assert(optreal->default_val < optreal->min ||
+ optreal->default_val > optreal->max);
+ break;
+ }
+
+ case RELOPT_TYPE_ENUM:
+ {
+ relopt_enum *optenum = (relopt_enum *) gen;
+
+ for (relopt_enum_elt_def *elt = optenum->members;
+ elt->string_val; elt++)
+ Assert(elt->symbol_val != optenum->default_val);
+ break;
+ }
+
+ default:
+ /* Neither bools nor strings can express "unset". */
+ Assert(false);
+ }
+ }
+}
+#endif /* USE_ASSERT_CHECKING */
+
/*
* initialize_reloptions
* initialization routine, must be called before parsing
@@ -730,6 +800,10 @@ initialize_reloptions(void)
/* flag the work is complete */
need_initialization = false;
+
+#ifdef USE_ASSERT_CHECKING
+ assert_toast_defaults_unsettable();
+#endif
}
/*
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 0aa5056918f..9c73c10fb11 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2836,7 +2836,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
* defaults, autovacuum's own first and plain vacuum second.
*/
- /* -1 in autovac setting means use log_autovacuum_min_duration */
+ /* a negative setting means use log_autovacuum_min_duration */
log_vacuum_min_duration = (avopts && avopts->log_vacuum_min_duration >= 0)
? avopts->log_vacuum_min_duration
: Log_autovacuum_min_duration;