From c2a987dde5297b63d2ddb71c7ab473135f9e9b8b Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Mon, 7 Sep 2026 15:32:34 +0900 Subject: [PATCH v14 9/9] Fix toast_tuple_find_biggest_attribute() for OID8 TOAST values This routine is used to decide if largest varlena attributes should be compressed or not, but stuck its decisions to a hardcoded TOAST_OID_POINTER_SIZE, which would be amiss when dealing with an oid8 TOAST value. This information is added to the parent Relation, and can be asked on a per-call basis of a new RelationGetToastChunkIdType(). --- src/include/utils/rel.h | 9 +++++ src/include/utils/relcache.h | 1 + src/backend/access/common/toast_internals.c | 3 +- src/backend/access/table/toast_helper.c | 18 ++++++++-- src/backend/utils/cache/relcache.c | 37 +++++++++++++++++++++ 5 files changed, 63 insertions(+), 5 deletions(-) diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index b7da03446a94..280a9b3ea21b 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -250,6 +250,15 @@ typedef struct RelationData */ Oid rd_toastoid; /* Real TOAST table's OID, or InvalidOid */ + /* + * Type OID of the "chunk_id" column of this relation's TOAST table, i.e. + * OIDOID or OID8OID. + * + * This data is filled on demand by RelationGetToastChunkIdType(), not + * at relcache build time, so as to save in syscache lookups. + */ + Oid rd_toastchunkidtype; + bool pgstat_enabled; /* should relation stats be counted */ /* use "struct" here to avoid needing to include pgstat.h: */ struct PgStat_RelationStatus *pgstat_info; /* statistics collection area */ diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index 89c27aa1529f..e17c84908332 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -57,6 +57,7 @@ extern List *RelationGetIndexList(Relation relation); extern List *RelationGetStatExtList(Relation relation); extern Oid RelationGetPrimaryKeyIndex(Relation relation, bool deferrable_ok); extern Oid RelationGetReplicaIndex(Relation relation); +extern Oid RelationGetToastChunkIdType(Relation relation); extern List *RelationGetIndexExpressions(Relation relation); extern List *RelationGetDummyIndexExpressions(Relation relation); extern List *RelationGetIndexPredicate(Relation relation); diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c index 7b37798e1c08..465f0d1e098f 100644 --- a/src/backend/access/common/toast_internals.c +++ b/src/backend/access/common/toast_internals.c @@ -25,7 +25,6 @@ #include "utils/fmgroids.h" #include "utils/rel.h" #include "utils/snapmgr.h" -#include "utils/lsyscache.h" static bool toastrel_valueid_exists(Relation toastrel, Oid8 valueid); static bool toastid_valueid_exists(Oid toastrelid, Oid8 valueid); @@ -131,7 +130,7 @@ toast_save_datum(Relation rel, Datum value, Pointer dval = DatumGetPointer(value); int num_indexes; int validIndex; - Oid toast_typid = get_atttype(rel->rd_rel->reltoastrelid, 1); + Oid toast_typid = RelationGetToastChunkIdType(rel); int32 max_chunk_size; /* Fields that will be assembled into the TOAST pointer at the end */ diff --git a/src/backend/access/table/toast_helper.c b/src/backend/access/table/toast_helper.c index 6c7257f5f94b..3235d033ea9b 100644 --- a/src/backend/access/table/toast_helper.c +++ b/src/backend/access/table/toast_helper.c @@ -175,8 +175,9 @@ toast_tuple_init(ToastTupleContext *ttc) * The column must have attstorage EXTERNAL or EXTENDED if check_main is * false, and must have attstorage MAIN if check_main is true. * - * The column must have a minimum size of MAXALIGN(TOAST_OID_POINTER_SIZE); - * if not, no benefit is to be expected by compressing it. + * The column must be larger than the TOAST pointer that would replace it; + * if not, no benefit is to be expected by compressing it. Note that this + * choice depends on the TOAST value type, oid or oid8. * * The return value is the index of the biggest suitable column, or * -1 if there is none. @@ -188,10 +189,21 @@ toast_tuple_find_biggest_attribute(ToastTupleContext *ttc, TupleDesc tupleDesc = ttc->ttc_rel->rd_att; int numAttrs = tupleDesc->natts; int biggest_attno = -1; - int32 biggest_size = MAXALIGN(TOAST_OID_POINTER_SIZE); + int32 biggest_size; int32 skip_colflags = TOASTCOL_IGNORE; int i; + /* + * Size of the TOAST pointer this relation would use. A relation without + * a TOAST table cannot have any of its attributes moved out-of-line, but + * it can still have some of them compressed. Fall back to the oid size + * in that case. + */ + if (RelationGetToastChunkIdType(ttc->ttc_rel) == OID8OID) + biggest_size = MAXALIGN(TOAST_OID8_POINTER_SIZE); + else + biggest_size = MAXALIGN(TOAST_OID_POINTER_SIZE); + if (for_compression) skip_colflags |= TOASTCOL_INCOMPRESSIBLE; diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index f475d7039772..65dfdbda100a 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -1205,6 +1205,9 @@ retry: relation->rd_fkeylist = NIL; relation->rd_fkeyvalid = false; + /* TOAST type data is not loaded till asked for */ + relation->rd_toastchunkidtype = InvalidOid; + /* partitioning data is not loaded till asked for */ relation->rd_partkey = NULL; relation->rd_partkeycxt = NULL; @@ -5093,6 +5096,39 @@ RelationGetReplicaIndex(Relation relation) return relation->rd_replidindex; } +/* + * RelationGetToastChunkIdType -- get the type of the relation's TOAST + * table "chunk_id" column + * + * Returns OIDOID or OID8OID, or InvalidOid if the relation has no TOAST + * table. + */ +Oid +RelationGetToastChunkIdType(Relation relation) +{ + Oid toastrelid = relation->rd_rel->reltoastrelid; + Oid typid; + + /* Quick exit if we already computed the value */ + if (OidIsValid(relation->rd_toastchunkidtype)) + return relation->rd_toastchunkidtype; + + /* Nothing to report without a TOAST table */ + if (!OidIsValid(toastrelid)) + return InvalidOid; + + typid = get_atttype(toastrelid, 1); + if (!OidIsValid(typid)) + elog(ERROR, "cache lookup failed for TOAST relation %u", + toastrelid); + if (typid != OIDOID && typid != OID8OID) + elog(ERROR, "unexpected type %u for chunk_id in TOAST relation %u", + typid, toastrelid); + + relation->rd_toastchunkidtype = typid; + return typid; +} + /* * RelationGetIndexExpressions -- get the index expressions for an index * @@ -6530,6 +6566,7 @@ load_relcache_init_file(bool shared) rel->rd_firstRelfilelocatorSubid = InvalidSubTransactionId; rel->rd_droppedSubid = InvalidSubTransactionId; rel->rd_amcache = NULL; + rel->rd_toastchunkidtype = InvalidOid; rel->pgstat_info = NULL; /* -- 2.55.0