1 /*-------------------------------------------------------------------------
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
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"
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.
54 AlterTableCreateToastTable(Oid relOid
, Datum reloptions
, bool force
)
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
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
77 BootstrapToastTable(char *relName
, Oid toastOid
, Oid toastIndexOid
)
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
)
87 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
88 errmsg("\"%s\" is not a table",
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",
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
108 create_toast_table(Relation rel
, Oid toastOid
, Oid toastIndexOid
,
109 Datum reloptions
, bool force
)
111 Oid relOid
= RelationGetRelid(rel
);
114 bool shared_relation
;
119 char toast_relname
[NAMEDATALEN
];
120 char toast_idxname
[NAMEDATALEN
];
121 IndexInfo
*indexInfo
;
122 Oid classObjectId
[2];
124 ObjectAddress baseobject
,
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())
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
)
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
152 if (!force
&& !needs_toast_table(rel
))
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,
169 TupleDescInitEntry(tupdesc
, (AttrNumber
) 2,
173 TupleDescInitEntry(tupdesc
, (AttrNumber
) 3,
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();
194 namespaceid
= PG_TOAST_NAMESPACE
;
196 toast_relid
= heap_create_with_catalog(toast_relname
,
198 rel
->rd_rel
->reltablespace
,
200 rel
->rd_rel
->relowner
,
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
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
;
245 toast_idxid
= index_create(toast_relid
, toast_idxname
, toastIndexOid
,
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
),
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
);
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
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();
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)".)
315 needs_toast_table(Relation rel
)
317 int32 data_length
= 0;
318 bool maxlength_unknown
= false;
319 bool has_toastable_attrs
= false;
321 Form_pg_attribute
*att
;
325 tupdesc
= rel
->rd_att
;
326 att
= tupdesc
->attrs
;
328 for (i
= 0; i
< tupdesc
->natts
; i
++)
330 if (att
[i
]->attisdropped
)
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
;
340 int32 maxlen
= type_maximum_size(att
[i
]->atttypid
,
344 maxlength_unknown
= true;
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
);