Update obsolete comment in index_drop(). When the comment was written,
[PostgreSQL.git] / src / backend / catalog / toasting.c
blobbca0c450a809d6dd8e362f94e3d2ed565a2bfcab
1 /*-------------------------------------------------------------------------
3 * toasting.c
4 * This file contains routines to support creation of toast tables
7 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
10 * IDENTIFICATION
11 * $PostgreSQL$
13 *-------------------------------------------------------------------------
15 #include "postgres.h"
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"
35 static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
36 Datum reloptions, bool force);
37 static bool needs_toast_table(Relation rel);
41 * AlterTableCreateToastTable
42 * If the table needs a toast table, and doesn't already have one,
43 * then create a toast table for it. (With the force option, make
44 * a toast table even if it appears unnecessary.)
46 * reloptions for the toast table can be passed, too. Pass (Datum) 0
47 * for default reloptions.
49 * We expect the caller to have verified that the relation is a table and have
50 * already done any necessary permission checks. Callers expect this function
51 * to end with CommandCounterIncrement if it makes any changes.
53 void
54 AlterTableCreateToastTable(Oid relOid, Datum reloptions, bool force)
56 Relation rel;
59 * Grab an exclusive lock on the target table, which we will NOT release
60 * until end of transaction. (This is probably redundant in all present
61 * uses...)
63 rel = heap_open(relOid, AccessExclusiveLock);
65 /* create_toast_table does all the work */
66 (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, force);
68 heap_close(rel, NoLock);
72 * Create a toast table during bootstrap
74 * Here we need to prespecify the OIDs of the toast table and its index
76 void
77 BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
79 Relation rel;
81 rel = heap_openrv(makeRangeVar(NULL, relName, -1), AccessExclusiveLock);
83 /* Note: during bootstrap may see uncataloged relation */
84 if (rel->rd_rel->relkind != RELKIND_RELATION &&
85 rel->rd_rel->relkind != RELKIND_UNCATALOGED)
86 ereport(ERROR,
87 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
88 errmsg("\"%s\" is not a table",
89 relName)));
91 /* create_toast_table does all the work */
92 if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0, false))
93 elog(ERROR, "\"%s\" does not require a toast table",
94 relName);
96 heap_close(rel, NoLock);
101 * create_toast_table --- internal workhorse
103 * rel is already opened and exclusive-locked
104 * toastOid and toastIndexOid are normally InvalidOid, but during
105 * bootstrap they can be nonzero to specify hand-assigned OIDs
107 static bool
108 create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
109 Datum reloptions, bool force)
111 Oid relOid = RelationGetRelid(rel);
112 HeapTuple reltup;
113 TupleDesc tupdesc;
114 bool shared_relation;
115 Relation class_rel;
116 Oid toast_relid;
117 Oid toast_idxid;
118 Oid namespaceid;
119 char toast_relname[NAMEDATALEN];
120 char toast_idxname[NAMEDATALEN];
121 IndexInfo *indexInfo;
122 Oid classObjectId[2];
123 int16 coloptions[2];
124 ObjectAddress baseobject,
125 toastobject;
128 * Toast table is shared if and only if its parent is.
130 * We cannot allow toasting a shared relation after initdb (because
131 * there's no way to mark it toasted in other databases' pg_class).
133 shared_relation = rel->rd_rel->relisshared;
134 if (shared_relation && !IsBootstrapProcessingMode())
135 ereport(ERROR,
136 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
137 errmsg("shared tables cannot be toasted after initdb")));
140 * Is it already toasted?
142 if (rel->rd_rel->reltoastrelid != InvalidOid)
143 return false;
146 * Check to see whether the table actually needs a TOAST table.
148 * Caller can optionally override this check. (Note: at present
149 * no callers in core Postgres do so, but this option is needed by
150 * pg_migrator.)
152 if (!force && !needs_toast_table(rel))
153 return false;
156 * Create the toast table and its index
158 snprintf(toast_relname, sizeof(toast_relname),
159 "pg_toast_%u", relOid);
160 snprintf(toast_idxname, sizeof(toast_idxname),
161 "pg_toast_%u_index", relOid);
163 /* this is pretty painful... need a tuple descriptor */
164 tupdesc = CreateTemplateTupleDesc(3, false);
165 TupleDescInitEntry(tupdesc, (AttrNumber) 1,
166 "chunk_id",
167 OIDOID,
168 -1, 0);
169 TupleDescInitEntry(tupdesc, (AttrNumber) 2,
170 "chunk_seq",
171 INT4OID,
172 -1, 0);
173 TupleDescInitEntry(tupdesc, (AttrNumber) 3,
174 "chunk_data",
175 BYTEAOID,
176 -1, 0);
179 * Ensure that the toast table doesn't itself get toasted, or we'll be
180 * toast :-(. This is essential for chunk_data because type bytea is
181 * toastable; hit the other two just to be sure.
183 tupdesc->attrs[0]->attstorage = 'p';
184 tupdesc->attrs[1]->attstorage = 'p';
185 tupdesc->attrs[2]->attstorage = 'p';
188 * Toast tables for regular relations go in pg_toast; those for temp
189 * relations go into the per-backend temp-toast-table namespace.
191 if (rel->rd_islocaltemp)
192 namespaceid = GetTempToastNamespace();
193 else
194 namespaceid = PG_TOAST_NAMESPACE;
196 toast_relid = heap_create_with_catalog(toast_relname,
197 namespaceid,
198 rel->rd_rel->reltablespace,
199 toastOid,
200 rel->rd_rel->relowner,
201 tupdesc,
202 NIL,
203 RELKIND_TOASTVALUE,
204 shared_relation,
205 true,
207 ONCOMMIT_NOOP,
208 reloptions,
209 true);
211 /* make the toast relation visible, else index creation will fail */
212 CommandCounterIncrement();
215 * Create unique index on chunk_id, chunk_seq.
217 * NOTE: the normal TOAST access routines could actually function with a
218 * single-column index on chunk_id only. However, the slice access
219 * routines use both columns for faster access to an individual chunk. In
220 * addition, we want it to be unique as a check against the possibility of
221 * duplicate TOAST chunk OIDs. The index might also be a little more
222 * efficient this way, since btree isn't all that happy with large numbers
223 * of equal keys.
226 indexInfo = makeNode(IndexInfo);
227 indexInfo->ii_NumIndexAttrs = 2;
228 indexInfo->ii_KeyAttrNumbers[0] = 1;
229 indexInfo->ii_KeyAttrNumbers[1] = 2;
230 indexInfo->ii_Expressions = NIL;
231 indexInfo->ii_ExpressionsState = NIL;
232 indexInfo->ii_Predicate = NIL;
233 indexInfo->ii_PredicateState = NIL;
234 indexInfo->ii_Unique = true;
235 indexInfo->ii_ReadyForInserts = true;
236 indexInfo->ii_Concurrent = false;
237 indexInfo->ii_BrokenHotChain = false;
239 classObjectId[0] = OID_BTREE_OPS_OID;
240 classObjectId[1] = INT4_BTREE_OPS_OID;
242 coloptions[0] = 0;
243 coloptions[1] = 0;
245 toast_idxid = index_create(toast_relid, toast_idxname, toastIndexOid,
246 indexInfo,
247 BTREE_AM_OID,
248 rel->rd_rel->reltablespace,
249 classObjectId, coloptions, (Datum) 0,
250 true, false, true, false, false);
253 * Store the toast table's OID in the parent relation's pg_class row
255 class_rel = heap_open(RelationRelationId, RowExclusiveLock);
257 reltup = SearchSysCacheCopy(RELOID,
258 ObjectIdGetDatum(relOid),
259 0, 0, 0);
260 if (!HeapTupleIsValid(reltup))
261 elog(ERROR, "cache lookup failed for relation %u", relOid);
263 ((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid = toast_relid;
265 if (!IsBootstrapProcessingMode())
267 /* normal case, use a transactional update */
268 simple_heap_update(class_rel, &reltup->t_self, reltup);
270 /* Keep catalog indexes current */
271 CatalogUpdateIndexes(class_rel, reltup);
273 else
275 /* While bootstrapping, we cannot UPDATE, so overwrite in-place */
276 heap_inplace_update(class_rel, reltup);
279 heap_freetuple(reltup);
281 heap_close(class_rel, RowExclusiveLock);
284 * Register dependency from the toast table to the master, so that the
285 * toast table will be deleted if the master is. Skip this in bootstrap
286 * mode.
288 if (!IsBootstrapProcessingMode())
290 baseobject.classId = RelationRelationId;
291 baseobject.objectId = relOid;
292 baseobject.objectSubId = 0;
293 toastobject.classId = RelationRelationId;
294 toastobject.objectId = toast_relid;
295 toastobject.objectSubId = 0;
297 recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
301 * Make changes visible
303 CommandCounterIncrement();
305 return true;
309 * Check to see whether the table needs a TOAST table. It does only if
310 * (1) there are any toastable attributes, and (2) the maximum length
311 * of a tuple could exceed TOAST_TUPLE_THRESHOLD. (We don't want to
312 * create a toast table for something like "f1 varchar(20)".)
314 static bool
315 needs_toast_table(Relation rel)
317 int32 data_length = 0;
318 bool maxlength_unknown = false;
319 bool has_toastable_attrs = false;
320 TupleDesc tupdesc;
321 Form_pg_attribute *att;
322 int32 tuple_length;
323 int i;
325 tupdesc = rel->rd_att;
326 att = tupdesc->attrs;
328 for (i = 0; i < tupdesc->natts; i++)
330 if (att[i]->attisdropped)
331 continue;
332 data_length = att_align_nominal(data_length, att[i]->attalign);
333 if (att[i]->attlen > 0)
335 /* Fixed-length types are never toastable */
336 data_length += att[i]->attlen;
338 else
340 int32 maxlen = type_maximum_size(att[i]->atttypid,
341 att[i]->atttypmod);
343 if (maxlen < 0)
344 maxlength_unknown = true;
345 else
346 data_length += maxlen;
347 if (att[i]->attstorage != 'p')
348 has_toastable_attrs = true;
351 if (!has_toastable_attrs)
352 return false; /* nothing to toast? */
353 if (maxlength_unknown)
354 return true; /* any unlimited-length attrs? */
355 tuple_length = MAXALIGN(offsetof(HeapTupleHeaderData, t_bits) +
356 BITMAPLEN(tupdesc->natts)) +
357 MAXALIGN(data_length);
358 return (tuple_length > TOAST_TUPLE_THRESHOLD);