1/*-------------------------------------------------------------------------
4 * This file contains routines to support creation of toast tables
7 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/catalog/toasting.c
13 *-------------------------------------------------------------------------
32#include "utils/fmgroids.h"
46 * CreateToastTable variants
47 * If the table needs a toast table, and doesn't already have one,
48 * then create a toast table for it.
50 * reloptions for the toast table can be passed, too. Pass (Datum) 0
51 * for default reloptions.
53 * We expect the caller to have verified that the relation is a table and have
54 * already done any necessary permission checks. Callers expect this function
55 * to end with CommandCounterIncrement if it makes any changes.
79 bool check,
Oid OIDOldToast)
85 /* create_toast_table does all the work */
93 * Create a toast table during bootstrap
95 * Here we need to prespecify the OIDs of the toast table and its index
104 if (rel->
rd_rel->relkind != RELKIND_RELATION &&
105 rel->
rd_rel->relkind != RELKIND_MATVIEW)
106 elog(
ERROR,
"\"%s\" is not a table or materialized view",
109 /* create_toast_table does all the work */
112 elog(
ERROR,
"\"%s\" does not require a toast table",
120 * create_toast_table --- internal workhorse
122 * rel is already opened and locked
123 * toastOid and toastIndexOid are normally InvalidOid, but during
124 * bootstrap they can be nonzero to specify hand-assigned OIDs
134 bool shared_relation;
135 bool mapped_relation;
150 * Is it already toasted?
156 * Check to see whether the table actually needs a TOAST table.
160 /* Normal mode, normal check */
167 * In binary-upgrade mode, create a TOAST table if and only if
168 * pg_upgrade told us to (ie, a TOAST table OID has been provided).
170 * This indicates that the old cluster had a TOAST table for the
171 * current table. We must create a TOAST table to receive the old
172 * TOAST file, even if the table seems not to need one.
174 * Contrariwise, if the old cluster did not have a TOAST table, we
175 * should be able to get along without one even if the new version's
176 * needs_toast_table rules suggest we should have one. There is a lot
177 * of daylight between where we will create a TOAST table and where
178 * one is really necessary to avoid failures, so small cross-version
179 * differences in the when-to-create heuristic shouldn't be a problem.
180 * If we tried to create a TOAST table anyway, we would have the
181 * problem that it might take up an OID that will conflict with some
182 * old-cluster table we haven't seen yet.
189 * If requested check lockmode is sufficient. This is a cross check in
190 * case of errors or conflicting decisions in earlier code.
193 elog(
ERROR,
"AccessExclusiveLock required to add toast table.");
196 * Create the toast table and its index
198 snprintf(toast_relname,
sizeof(toast_relname),
199 "pg_toast_%u", relOid);
200 snprintf(toast_idxname,
sizeof(toast_idxname),
201 "pg_toast_%u_index", relOid);
203 /* this is pretty painful... need a tuple descriptor */
219 * Ensure that the toast table doesn't itself get toasted, or we'll be
220 * toast :-(. This is essential for chunk_data because type bytea is
221 * toastable; hit the other two just to be sure.
227 /* Toast field should not be compressed */
233 * Toast tables for regular relations go in pg_toast; those for temp
234 * relations go into the per-backend temp-toast-table namespace.
239 namespaceid = PG_TOAST_NAMESPACE;
241 /* Toast table is shared if and only if its parent is. */
242 shared_relation = rel->
rd_rel->relisshared;
244 /* It's mapped if and only if its parent is, too */
249 rel->
rd_rel->reltablespace,
258 rel->
rd_rel->relpersistence,
270 /* make the toast relation visible, else table_open will fail */
273 /* ShareLock is not really needed here, but take it anyway */
277 * Create unique index on chunk_id, chunk_seq.
279 * NOTE: the normal TOAST access routines could actually function with a
280 * single-column index on chunk_id only. However, the slice access
281 * routines use both columns for faster access to an individual chunk. In
282 * addition, we want it to be unique as a check against the possibility of
283 * duplicate TOAST chunk OIDs. The index might also be a little more
284 * efficient this way, since btree isn't all that happy with large numbers
308 indexInfo->
ii_Am = BTREE_AM_OID;
315 opclassIds[0] = OID_BTREE_OPS_OID;
316 opclassIds[1] = INT4_BTREE_OPS_OID;
326 rel->
rd_rel->reltablespace,
327 collationIds, opclassIds, NULL, coloptions, NULL, (
Datum) 0,
333 * Store the toast table's OID in the parent relation's pg_class row
339 /* normal case, use a transactional update */
342 elog(
ERROR,
"cache lookup failed for relation %u", relOid);
350 /* While bootstrapping, we cannot UPDATE, so overwrite in-place */
362 elog(
ERROR,
"cache lookup failed for relation %u", relOid);
374 * Register dependency from the toast table to the main, so that the toast
375 * table will be deleted if the main is. Skip this in bootstrap mode.
379 baseobject.
classId = RelationRelationId;
382 toastobject.
classId = RelationRelationId;
390 * Make changes visible
398 * Check to see whether the table needs a TOAST table.
404 * No need to create a TOAST table for partitioned tables.
406 if (rel->
rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
410 * We cannot allow toasting a shared relation after initdb (because
411 * there's no way to mark it toasted in other databases' pg_class).
417 * Ignore attempts to create toast tables on catalog tables after initdb.
418 * Which catalogs get toast tables is explicitly chosen in catalog/pg_*.h.
419 * (We could get here via some ALTER TABLE command if the catalog doesn't
420 * have a toast table.)
425 /* Otherwise, let the AM decide. */
#define OidIsValid(objectId)
bool IsCatalogRelation(Relation relation)
void systable_inplace_update_begin(Relation relation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, const ScanKeyData *key, HeapTuple *oldtupcopy, void **state)
void systable_inplace_update_finish(void *state, HeapTuple tuple)
Assert(PointerIsAligned(start, uint64))
Oid heap_create_with_catalog(const char *relname, Oid relnamespace, Oid reltablespace, Oid relid, Oid reltypeid, Oid reloftypeid, Oid ownerid, Oid accessmtd, TupleDesc tupdesc, List *cooked_constraints, char relkind, char relpersistence, bool shared_relation, bool mapped_relation, OnCommitAction oncommit, Datum reloptions, bool use_user_acl, bool allow_system_table_mods, bool is_internal, Oid relrewrite, ObjectAddress *typaddress)
Oid binary_upgrade_next_toast_pg_class_oid
void heap_freetuple(HeapTuple htup)
#define HeapTupleIsValid(tuple)
static void * GETSTRUCT(const HeapTupleData *tuple)
Oid index_create(Relation heapRelation, const char *indexRelationName, Oid indexRelationId, Oid parentIndexRelid, Oid parentConstraintId, RelFileNumber relFileNumber, IndexInfo *indexInfo, const List *indexColNames, Oid accessMethodId, Oid tableSpaceId, const Oid *collationIds, const Oid *opclassIds, const Datum *opclassOptions, const int16 *coloptions, const NullableDatum *stattargets, Datum reloptions, bits16 flags, bits16 constr_flags, bool allow_system_table_mods, bool is_internal, Oid *constraintId)
#define INDEX_CREATE_IS_PRIMARY
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
#define AccessExclusiveLock
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
MemoryContext CurrentMemoryContext
#define IsBootstrapProcessingMode()
bool isTempOrTempToastNamespace(Oid namespaceId)
Oid GetTempToastNamespace(void)
FormData_pg_class * Form_pg_class
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
#define list_make2(x1, x2)
static Datum ObjectIdGetDatum(Oid X)
#define RelationGetRelid(relation)
#define RelationIsMapped(relation)
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
#define BTEqualStrategyNumber
uint16 * ii_ExclusionStrats
ExprState * ii_PredicateState
List * ii_ExpressionsState
AttrNumber ii_IndexAttrNumbers[INDEX_MAX_KEYS]
#define SearchSysCacheCopy1(cacheId, key1)
void table_close(Relation relation, LOCKMODE lockmode)
Relation table_open(Oid relationId, LOCKMODE lockmode)
Relation table_openrv(const RangeVar *relation, LOCKMODE lockmode)
static bool table_relation_needs_toast_table(Relation rel)
static Oid table_relation_toast_am(Relation rel)
#define InvalidCompressionMethod
void BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
static void CheckAndCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, bool check, Oid OIDOldToast)
void NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, Oid OIDOldToast)
void NewRelationCreateToastTable(Oid relOid, Datum reloptions)
void AlterTableCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions, LOCKMODE lockmode, bool check, Oid OIDOldToast)
static bool needs_toast_table(Relation rel)
TupleDesc CreateTemplateTupleDesc(int natts)
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
void CommandCounterIncrement(void)