1 /*-------------------------------------------------------------------------
4 * This file contains routines to support creation of toast tables
7 * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/catalog/toasting.c
13 *-------------------------------------------------------------------------
17 #include "access/heapam.h"
18 #include "access/tuptoaster.h"
19 #include "access/xact.h"
20 #include "catalog/dependency.h"
21 #include "catalog/heap.h"
22 #include "catalog/index.h"
23 #include "catalog/indexing.h"
24 #include "catalog/namespace.h"
25 #include "catalog/pg_namespace.h"
26 #include "catalog/pg_opclass.h"
27 #include "catalog/pg_type.h"
28 #include "catalog/toasting.h"
29 #include "miscadmin.h"
30 #include "nodes/makefuncs.h"
31 #include "utils/builtins.h"
32 #include "utils/syscache.h"
34 /* Kluges for upgrade-in-place support */
35 extern Oid binary_upgrade_next_toast_relfilenode;
37 Oid binary_upgrade_next_pg_type_toast_oid = InvalidOid;
39 static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
41 static bool needs_toast_table(Relation rel);
45 * AlterTableCreateToastTable
46 * If the table needs a toast table, and doesn't already have one,
47 * then create a toast table for it.
49 * reloptions for the toast table can be passed, too. Pass (Datum) 0
50 * for default reloptions.
52 * We expect the caller to have verified that the relation is a table and have
53 * already done any necessary permission checks. Callers expect this function
54 * to end with CommandCounterIncrement if it makes any changes.
57 AlterTableCreateToastTable(Oid relOid, Datum reloptions)
62 * Grab an exclusive lock on the target table, which we will NOT release
63 * until end of transaction. (This is probably redundant in all present
66 rel = heap_open(relOid, AccessExclusiveLock);
68 /* create_toast_table does all the work */
69 (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions);
71 heap_close(rel, NoLock);
75 * Create a toast table during bootstrap
77 * Here we need to prespecify the OIDs of the toast table and its index
80 BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
84 rel = heap_openrv(makeRangeVar(NULL, relName, -1), AccessExclusiveLock);
86 /* Note: during bootstrap may see uncataloged relation */
87 if (rel->rd_rel->relkind != RELKIND_RELATION &&
88 rel->rd_rel->relkind != RELKIND_UNCATALOGED)
90 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
91 errmsg("\"%s\" is not a table",
94 /* create_toast_table does all the work */
95 if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0))
96 elog(ERROR, "\"%s\" does not require a toast table",
99 heap_close(rel, NoLock);
104 * create_toast_table --- internal workhorse
106 * rel is already opened and exclusive-locked
107 * toastOid and toastIndexOid are normally InvalidOid, but during
108 * bootstrap they can be nonzero to specify hand-assigned OIDs
111 create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions)
113 Oid relOid = RelationGetRelid(rel);
116 bool shared_relation;
117 bool mapped_relation;
121 Oid toast_typid = InvalidOid;
123 char toast_relname[NAMEDATALEN];
124 char toast_idxname[NAMEDATALEN];
125 IndexInfo *indexInfo;
126 Oid classObjectId[2];
128 ObjectAddress baseobject,
132 * Toast table is shared if and only if its parent is.
134 * We cannot allow toasting a shared relation after initdb (because
135 * there's no way to mark it toasted in other databases' pg_class).
137 shared_relation = rel->rd_rel->relisshared;
138 if (shared_relation && !IsBootstrapProcessingMode())
140 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
141 errmsg("shared tables cannot be toasted after initdb")));
143 /* It's mapped if and only if its parent is, too */
144 mapped_relation = RelationIsMapped(rel);
147 * Is it already toasted?
149 if (rel->rd_rel->reltoastrelid != InvalidOid)
153 * Check to see whether the table actually needs a TOAST table.
155 * If an update-in-place toast relfilenode is specified, force toast file
156 * creation even if it seems not to need one.
158 if (!needs_toast_table(rel) &&
159 !OidIsValid(binary_upgrade_next_toast_relfilenode))
163 * Create the toast table and its index
165 snprintf(toast_relname, sizeof(toast_relname),
166 "pg_toast_%u", relOid);
167 snprintf(toast_idxname, sizeof(toast_idxname),
168 "pg_toast_%u_index", relOid);
170 /* this is pretty painful... need a tuple descriptor */
171 tupdesc = CreateTemplateTupleDesc(3, false);
172 TupleDescInitEntry(tupdesc, (AttrNumber) 1,
176 TupleDescInitEntry(tupdesc, (AttrNumber) 2,
180 TupleDescInitEntry(tupdesc, (AttrNumber) 3,
186 * Ensure that the toast table doesn't itself get toasted, or we'll be
187 * toast :-(. This is essential for chunk_data because type bytea is
188 * toastable; hit the other two just to be sure.
190 tupdesc->attrs[0]->attstorage = 'p';
191 tupdesc->attrs[1]->attstorage = 'p';
192 tupdesc->attrs[2]->attstorage = 'p';
195 * Toast tables for regular relations go in pg_toast; those for temp
196 * relations go into the per-backend temp-toast-table namespace.
198 if (RelationUsesTempNamespace(rel))
199 namespaceid = GetTempToastNamespace();
201 namespaceid = PG_TOAST_NAMESPACE;
203 if (OidIsValid(binary_upgrade_next_pg_type_toast_oid))
205 toast_typid = binary_upgrade_next_pg_type_toast_oid;
206 binary_upgrade_next_pg_type_toast_oid = InvalidOid;
209 toast_relid = heap_create_with_catalog(toast_relname,
211 rel->rd_rel->reltablespace,
215 rel->rd_rel->relowner,
219 rel->rd_rel->relpersistence,
229 Assert(toast_relid != InvalidOid);
231 /* make the toast relation visible, else index creation will fail */
232 CommandCounterIncrement();
235 * Create unique index on chunk_id, chunk_seq.
237 * NOTE: the normal TOAST access routines could actually function with a
238 * single-column index on chunk_id only. However, the slice access
239 * routines use both columns for faster access to an individual chunk. In
240 * addition, we want it to be unique as a check against the possibility of
241 * duplicate TOAST chunk OIDs. The index might also be a little more
242 * efficient this way, since btree isn't all that happy with large numbers
246 indexInfo = makeNode(IndexInfo);
247 indexInfo->ii_NumIndexAttrs = 2;
248 indexInfo->ii_KeyAttrNumbers[0] = 1;
249 indexInfo->ii_KeyAttrNumbers[1] = 2;
250 indexInfo->ii_Expressions = NIL;
251 indexInfo->ii_ExpressionsState = NIL;
252 indexInfo->ii_Predicate = NIL;
253 indexInfo->ii_PredicateState = NIL;
254 indexInfo->ii_ExclusionOps = NULL;
255 indexInfo->ii_ExclusionProcs = NULL;
256 indexInfo->ii_ExclusionStrats = NULL;
257 indexInfo->ii_Unique = true;
258 indexInfo->ii_ReadyForInserts = true;
259 indexInfo->ii_Concurrent = false;
260 indexInfo->ii_BrokenHotChain = false;
262 classObjectId[0] = OID_BTREE_OPS_OID;
263 classObjectId[1] = INT4_BTREE_OPS_OID;
268 toast_idxid = index_create(toast_relid, toast_idxname, toastIndexOid,
270 list_make2("chunk_id", "chunk_seq"),
272 rel->rd_rel->reltablespace,
273 classObjectId, coloptions, (Datum) 0,
274 true, false, false, false,
278 * Store the toast table's OID in the parent relation's pg_class row
280 class_rel = heap_open(RelationRelationId, RowExclusiveLock);
282 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
283 if (!HeapTupleIsValid(reltup))
284 elog(ERROR, "cache lookup failed for relation %u", relOid);
286 ((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid = toast_relid;
288 if (!IsBootstrapProcessingMode())
290 /* normal case, use a transactional update */
291 simple_heap_update(class_rel, &reltup->t_self, reltup);
293 /* Keep catalog indexes current */
294 CatalogUpdateIndexes(class_rel, reltup);
298 /* While bootstrapping, we cannot UPDATE, so overwrite in-place */
299 heap_inplace_update(class_rel, reltup);
302 heap_freetuple(reltup);
304 heap_close(class_rel, RowExclusiveLock);
307 * Register dependency from the toast table to the master, so that the
308 * toast table will be deleted if the master is. Skip this in bootstrap
311 if (!IsBootstrapProcessingMode())
313 baseobject.classId = RelationRelationId;
314 baseobject.objectId = relOid;
315 baseobject.objectSubId = 0;
316 toastobject.classId = RelationRelationId;
317 toastobject.objectId = toast_relid;
318 toastobject.objectSubId = 0;
320 recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
324 * Make changes visible
326 CommandCounterIncrement();
332 * Check to see whether the table needs a TOAST table. It does only if
333 * (1) there are any toastable attributes, and (2) the maximum length
334 * of a tuple could exceed TOAST_TUPLE_THRESHOLD. (We don't want to
335 * create a toast table for something like "f1 varchar(20)".)
338 needs_toast_table(Relation rel)
340 int32 data_length = 0;
341 bool maxlength_unknown = false;
342 bool has_toastable_attrs = false;
344 Form_pg_attribute *att;
348 tupdesc = rel->rd_att;
349 att = tupdesc->attrs;
351 for (i = 0; i < tupdesc->natts; i++)
353 if (att[i]->attisdropped)
355 data_length = att_align_nominal(data_length, att[i]->attalign);
356 if (att[i]->attlen > 0)
358 /* Fixed-length types are never toastable */
359 data_length += att[i]->attlen;
363 int32 maxlen = type_maximum_size(att[i]->atttypid,
367 maxlength_unknown = true;
369 data_length += maxlen;
370 if (att[i]->attstorage != 'p')
371 has_toastable_attrs = true;
374 if (!has_toastable_attrs)
375 return false; /* nothing to toast? */
376 if (maxlength_unknown)
377 return true; /* any unlimited-length attrs? */
378 tuple_length = MAXALIGN(offsetof(HeapTupleHeaderData, t_bits) +
379 BITMAPLEN(tupdesc->natts)) +
380 MAXALIGN(data_length);
381 return (tuple_length > TOAST_TUPLE_THRESHOLD);