From f33844889bb6950f9c207b9ea4830947f1e1f50e Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Fri, 11 Sep 2026 12:26:58 +0900 Subject: [PATCH] Add relation option toast_value_type This relation option gives the possibility to define the attribute type that can be used for chunk_id in a TOAST table when it is initially created. An update of this parameter has no effect if a TOAST table already exists under the relation, even on rewrites. It cannot be set for TOAST relations. This new option can be set only to "oid" as of this commit. This feature is extracted as a patch of its own to ease the introduction of a second mode in a follow-up change, related to the introduction of 8-byte OIDs in external TOAST pointers. Bump catalog version, as StdRdOptions has changed. Author: Michael Paquier Reviewed-by: Greg Burd Reviewed-by: Bharath Rupireddy Reviewed-by: Yugo Nagata Discussion: https://postgr.es/m/af19kUjwjhaoUTLn@paquier.xyz --- doc/src/sgml/ref/create_table.sgml | 26 ++++++++++++++++++++++++ src/backend/access/common/reloptions.c | 21 +++++++++++++++++++ src/backend/catalog/toasting.c | 16 +++++++++++++++ src/bin/psql/tab-complete.in.c | 1 + src/include/catalog/catversion.h | 2 +- src/include/utils/rel.h | 17 ++++++++++++++++ src/test/regress/expected/reloptions.out | 22 ++++++++++++++++++++ src/test/regress/sql/reloptions.sql | 12 +++++++++++ src/tools/pgindent/typedefs.list | 1 + 9 files changed, 117 insertions(+), 1 deletion(-) diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml index e342585c7f0..79622c1a23d 100644 --- a/doc/src/sgml/ref/create_table.sgml +++ b/doc/src/sgml/ref/create_table.sgml @@ -1653,6 +1653,32 @@ WITH ( MODULUS numeric_literal, REM + + toast_value_type (enum) + + toast_value_type storage parameter + + + + + Specifies the attribute type of chunk_id to use when + creating a TOAST relation for this table. The + default is oid. + This parameter cannot be set for TOAST tables. + + + The value is only consulted when the TOAST relation + is created. Changing it afterwards has no effect on an existing + TOAST relation, and a table rewrite such as + CLUSTER or + VACUUM FULL + preserves the type already in use. A dump and restore, on the other + hand, recreates the TOAST relation and therefore + applies the current value of this parameter. + + + + parallel_workers (integer) diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c index c3f6f45f460..82491a31a9a 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -548,6 +548,14 @@ static relopt_enum_elt_def viewCheckOptValues[] = {(const char *) NULL} /* list terminator */ }; +/* values from StdRdOptToastValueType */ +static relopt_enum_elt_def StdRdOptToastValueTypes[] = +{ + /* no value for INVALID */ + {"oid", STDRD_OPTION_TOAST_VALUE_TYPE_OID}, + {(const char *) NULL} /* list terminator */ +}; + static relopt_enum enumRelOpts[] = { { @@ -561,6 +569,17 @@ static relopt_enum enumRelOpts[] = STDRD_OPTION_VACUUM_INDEX_CLEANUP_NOT_SET, gettext_noop("Valid values are \"on\", \"off\", and \"auto\".") }, + { + { + "toast_value_type", + "Controls the attribute type of chunk_id at toast table creation", + RELOPT_KIND_HEAP, + ShareUpdateExclusiveLock + }, + StdRdOptToastValueTypes, + STDRD_OPTION_TOAST_VALUE_TYPE_OID, + gettext_noop("Valid values are \"oid\".") + }, { { "buffering", @@ -2078,6 +2097,8 @@ static const relopt_parse_elt stdRdOptionsTab[] = { offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_analyze_min_duration)}, {"toast_tuple_target", RELOPT_TYPE_INT, offsetof(StdRdOptions, toast_tuple_target)}, + {"toast_value_type", RELOPT_TYPE_ENUM, + offsetof(StdRdOptions, toast_value_type)}, {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_REAL, offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay)}, {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL, diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index b19478ba072..a4a7b7c91b3 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -159,9 +159,25 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, */ if (!IsBinaryUpgrade) { + StdRdOptToastValueType value_type; + /* Normal mode, normal check */ if (!needs_toast_table(rel)) return false; + + value_type = RelationGetToastValueType(rel, STDRD_OPTION_TOAST_VALUE_TYPE_OID); + + /* no default clause to catch new values added */ + switch (value_type) + { + case STDRD_OPTION_TOAST_VALUE_TYPE_OID: + toast_chunkid_typid = OIDOID; + break; + case STDRD_OPTION_TOAST_VALUE_TYPE_INVALID: + elog(ERROR, "unexpected toast_value_type value %d", + value_type); + break; + } } else { diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c index 8e1a1d69740..84a129efff5 100644 --- a/src/bin/psql/tab-complete.in.c +++ b/src/bin/psql/tab-complete.in.c @@ -1441,6 +1441,7 @@ static const char *const table_storage_parameters[] = { "toast.vacuum_max_eager_freeze_failure_rate", "toast.vacuum_truncate", "toast_tuple_target", + "toast_value_type", "user_catalog_table", "vacuum_index_cleanup", "vacuum_max_eager_freeze_failure_rate", diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 12ae8f4331a..97d367cdd42 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -57,6 +57,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202609111 +#define CATALOG_VERSION_NO 202609112 #endif diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 41ab4586c6b..a88549dcc55 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -341,11 +341,20 @@ typedef enum StdRdOptIndexCleanup STDRD_OPTION_VACUUM_INDEX_CLEANUP_NOT_SET, } StdRdOptIndexCleanup; +/* StdRdOptions->toast_value_type values */ +typedef enum StdRdOptToastValueType +{ + STDRD_OPTION_TOAST_VALUE_TYPE_INVALID = 0, + STDRD_OPTION_TOAST_VALUE_TYPE_OID, +} StdRdOptToastValueType; + typedef struct StdRdOptions { int32 vl_len_; /* varlena header (do not touch directly!) */ int fillfactor; /* page fill factor in percent (0..100) */ int toast_tuple_target; /* target for tuple toasting */ + StdRdOptToastValueType toast_value_type; /* type assigned to chunk_id + * at toast table creation */ AutoVacOpts autovacuum; /* autovacuum-related options */ bool user_catalog_table; /* use as an additional catalog relation */ int parallel_workers; /* max number of parallel workers */ @@ -370,6 +379,14 @@ typedef struct StdRdOptions ((relation)->rd_options ? \ ((StdRdOptions *) (relation)->rd_options)->toast_tuple_target : (defaulttarg)) +/* + * RelationGetToastValueType + * Returns the relation's toast_value_type. Note multiple eval of argument! + */ +#define RelationGetToastValueType(relation, defaulttarg) \ + ((relation)->rd_options ? \ + ((StdRdOptions *) (relation)->rd_options)->toast_value_type : (defaulttarg)) + /* * RelationGetFillFactor * Returns the relation's fillfactor. Note multiple eval of argument! diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out index e3a974f2611..9b4c8587aa4 100644 --- a/src/test/regress/expected/reloptions.out +++ b/src/test/regress/expected/reloptions.out @@ -194,6 +194,28 @@ SELECT reloptions FROM pg_class WHERE oid = :toast_oid; -- Fail on non-existent options in toast namespace CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42); ERROR: unrecognized parameter "not_existing_option" +-- Test toast_value_type. +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.toast_value_type = 'oid'); +ERROR: unrecognized parameter "toast_value_type" +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast_value_type = 'int8'); +ERROR: invalid value for enum option "toast_value_type": int8 +DETAIL: Valid values are "oid". +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast_value_type = 'oid'); +SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test2'::regclass; + reloptions +------------------------ + {toast_value_type=oid} +(1 row) + +DROP TABLE reloptions_test2; +-- relkinds not supported. +CREATE INDEX reloptions_test_idx0 ON reloptions_test (s) + WITH (toast_value_type = 'oid'); +ERROR: unrecognized parameter "toast_value_type" +CREATE TABLE reloptions_test2 (i int) PARTITION BY RANGE (i) + WITH (toast_value_type = 'oid'); +ERROR: cannot specify storage parameters for a partitioned table +HINT: Specify storage parameters for its leaf partitions instead. -- Mix TOAST & heap DROP TABLE reloptions_test; CREATE TABLE reloptions_test (s VARCHAR) WITH diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sql index 680c8bf8614..503d15d16e5 100644 --- a/src/test/regress/sql/reloptions.sql +++ b/src/test/regress/sql/reloptions.sql @@ -112,6 +112,18 @@ SELECT reloptions FROM pg_class WHERE oid = :toast_oid; -- Fail on non-existent options in toast namespace CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42); +-- Test toast_value_type. +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.toast_value_type = 'oid'); +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast_value_type = 'int8'); +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast_value_type = 'oid'); +SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test2'::regclass; +DROP TABLE reloptions_test2; +-- relkinds not supported. +CREATE INDEX reloptions_test_idx0 ON reloptions_test (s) + WITH (toast_value_type = 'oid'); +CREATE TABLE reloptions_test2 (i int) PARTITION BY RANGE (i) + WITH (toast_value_type = 'oid'); + -- Mix TOAST & heap DROP TABLE reloptions_test; diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 7aedaafab90..de21cea65f9 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -3000,6 +3000,7 @@ StatsElem StatsExtInfo StdAnalyzeData StdRdOptIndexCleanup +StdRdOptToastValueType StdRdOptions Step StopList