1 /*-------------------------------------------------------------------------
4 * This file contains routines to support creation of toast tables
7 * Portions Copyright (c) 1996-2008, 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 static bool needs_toast_table(Relation rel
);
40 * AlterTableCreateToastTable
41 * If the table needs a toast table, and doesn't already have one,
42 * then create a toast table for it.
44 * We expect the caller to have verified that the relation is a table and have
45 * already done any necessary permission checks. Callers expect this function
46 * to end with CommandCounterIncrement if it makes any changes.
49 AlterTableCreateToastTable(Oid relOid
)
54 * Grab an exclusive lock on the target table, which we will NOT release
55 * until end of transaction. (This is probably redundant in all present
58 rel
= heap_open(relOid
, AccessExclusiveLock
);
60 /* create_toast_table does all the work */
61 (void) create_toast_table(rel
, InvalidOid
, InvalidOid
);
63 heap_close(rel
, NoLock
);
67 * Create a toast table during bootstrap
69 * Here we need to prespecify the OIDs of the toast table and its index
72 BootstrapToastTable(char *relName
, Oid toastOid
, Oid toastIndexOid
)
76 rel
= heap_openrv(makeRangeVar(NULL
, relName
, -1), AccessExclusiveLock
);
78 /* Note: during bootstrap may see uncataloged relation */
79 if (rel
->rd_rel
->relkind
!= RELKIND_RELATION
&&
80 rel
->rd_rel
->relkind
!= RELKIND_UNCATALOGED
)
82 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
83 errmsg("\"%s\" is not a table",
86 /* create_toast_table does all the work */
87 if (!create_toast_table(rel
, toastOid
, toastIndexOid
))
88 elog(ERROR
, "\"%s\" does not require a toast table",
91 heap_close(rel
, NoLock
);
96 * create_toast_table --- internal workhorse
98 * rel is already opened and exclusive-locked
99 * toastOid and toastIndexOid are normally InvalidOid, but during
100 * bootstrap they can be nonzero to specify hand-assigned OIDs
103 create_toast_table(Relation rel
, Oid toastOid
, Oid toastIndexOid
)
105 Oid relOid
= RelationGetRelid(rel
);
108 bool shared_relation
;
113 char toast_relname
[NAMEDATALEN
];
114 char toast_idxname
[NAMEDATALEN
];
115 IndexInfo
*indexInfo
;
116 Oid classObjectId
[2];
118 ObjectAddress baseobject
,
122 * Toast table is shared if and only if its parent is.
124 * We cannot allow toasting a shared relation after initdb (because
125 * there's no way to mark it toasted in other databases' pg_class).
127 shared_relation
= rel
->rd_rel
->relisshared
;
128 if (shared_relation
&& !IsBootstrapProcessingMode())
130 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
131 errmsg("shared tables cannot be toasted after initdb")));
134 * Is it already toasted?
136 if (rel
->rd_rel
->reltoastrelid
!= InvalidOid
)
140 * Check to see whether the table actually needs a TOAST table.
142 if (!needs_toast_table(rel
))
146 * Create the toast table and its index
148 snprintf(toast_relname
, sizeof(toast_relname
),
149 "pg_toast_%u", relOid
);
150 snprintf(toast_idxname
, sizeof(toast_idxname
),
151 "pg_toast_%u_index", relOid
);
153 /* this is pretty painful... need a tuple descriptor */
154 tupdesc
= CreateTemplateTupleDesc(3, false);
155 TupleDescInitEntry(tupdesc
, (AttrNumber
) 1,
159 TupleDescInitEntry(tupdesc
, (AttrNumber
) 2,
163 TupleDescInitEntry(tupdesc
, (AttrNumber
) 3,
169 * Ensure that the toast table doesn't itself get toasted, or we'll be
170 * toast :-(. This is essential for chunk_data because type bytea is
171 * toastable; hit the other two just to be sure.
173 tupdesc
->attrs
[0]->attstorage
= 'p';
174 tupdesc
->attrs
[1]->attstorage
= 'p';
175 tupdesc
->attrs
[2]->attstorage
= 'p';
178 * Toast tables for regular relations go in pg_toast; those for temp
179 * relations go into the per-backend temp-toast-table namespace.
182 namespaceid
= GetTempToastNamespace();
184 namespaceid
= PG_TOAST_NAMESPACE
;
187 * XXX would it make sense to apply the master's reloptions to the toast
188 * table? Or maybe some toast-specific reloptions?
190 toast_relid
= heap_create_with_catalog(toast_relname
,
192 rel
->rd_rel
->reltablespace
,
194 rel
->rd_rel
->relowner
,
205 /* make the toast relation visible, else index creation will fail */
206 CommandCounterIncrement();
209 * Create unique index on chunk_id, chunk_seq.
211 * NOTE: the normal TOAST access routines could actually function with a
212 * single-column index on chunk_id only. However, the slice access
213 * routines use both columns for faster access to an individual chunk. In
214 * addition, we want it to be unique as a check against the possibility of
215 * duplicate TOAST chunk OIDs. The index might also be a little more
216 * efficient this way, since btree isn't all that happy with large numbers
220 indexInfo
= makeNode(IndexInfo
);
221 indexInfo
->ii_NumIndexAttrs
= 2;
222 indexInfo
->ii_KeyAttrNumbers
[0] = 1;
223 indexInfo
->ii_KeyAttrNumbers
[1] = 2;
224 indexInfo
->ii_Expressions
= NIL
;
225 indexInfo
->ii_ExpressionsState
= NIL
;
226 indexInfo
->ii_Predicate
= NIL
;
227 indexInfo
->ii_PredicateState
= NIL
;
228 indexInfo
->ii_Unique
= true;
229 indexInfo
->ii_ReadyForInserts
= true;
230 indexInfo
->ii_Concurrent
= false;
231 indexInfo
->ii_BrokenHotChain
= false;
233 classObjectId
[0] = OID_BTREE_OPS_OID
;
234 classObjectId
[1] = INT4_BTREE_OPS_OID
;
239 toast_idxid
= index_create(toast_relid
, toast_idxname
, toastIndexOid
,
242 rel
->rd_rel
->reltablespace
,
243 classObjectId
, coloptions
, (Datum
) 0,
244 true, false, true, false, false);
247 * Store the toast table's OID in the parent relation's pg_class row
249 class_rel
= heap_open(RelationRelationId
, RowExclusiveLock
);
251 reltup
= SearchSysCacheCopy(RELOID
,
252 ObjectIdGetDatum(relOid
),
254 if (!HeapTupleIsValid(reltup
))
255 elog(ERROR
, "cache lookup failed for relation %u", relOid
);
257 ((Form_pg_class
) GETSTRUCT(reltup
))->reltoastrelid
= toast_relid
;
259 if (!IsBootstrapProcessingMode())
261 /* normal case, use a transactional update */
262 simple_heap_update(class_rel
, &reltup
->t_self
, reltup
);
264 /* Keep catalog indexes current */
265 CatalogUpdateIndexes(class_rel
, reltup
);
269 /* While bootstrapping, we cannot UPDATE, so overwrite in-place */
270 heap_inplace_update(class_rel
, reltup
);
273 heap_freetuple(reltup
);
275 heap_close(class_rel
, RowExclusiveLock
);
278 * Register dependency from the toast table to the master, so that the
279 * toast table will be deleted if the master is. Skip this in bootstrap
282 if (!IsBootstrapProcessingMode())
284 baseobject
.classId
= RelationRelationId
;
285 baseobject
.objectId
= relOid
;
286 baseobject
.objectSubId
= 0;
287 toastobject
.classId
= RelationRelationId
;
288 toastobject
.objectId
= toast_relid
;
289 toastobject
.objectSubId
= 0;
291 recordDependencyOn(&toastobject
, &baseobject
, DEPENDENCY_INTERNAL
);
295 * Make changes visible
297 CommandCounterIncrement();
303 * Check to see whether the table needs a TOAST table. It does only if
304 * (1) there are any toastable attributes, and (2) the maximum length
305 * of a tuple could exceed TOAST_TUPLE_THRESHOLD. (We don't want to
306 * create a toast table for something like "f1 varchar(20)".)
309 needs_toast_table(Relation rel
)
311 int32 data_length
= 0;
312 bool maxlength_unknown
= false;
313 bool has_toastable_attrs
= false;
315 Form_pg_attribute
*att
;
319 tupdesc
= rel
->rd_att
;
320 att
= tupdesc
->attrs
;
322 for (i
= 0; i
< tupdesc
->natts
; i
++)
324 if (att
[i
]->attisdropped
)
326 data_length
= att_align_nominal(data_length
, att
[i
]->attalign
);
327 if (att
[i
]->attlen
> 0)
329 /* Fixed-length types are never toastable */
330 data_length
+= att
[i
]->attlen
;
334 int32 maxlen
= type_maximum_size(att
[i
]->atttypid
,
338 maxlength_unknown
= true;
340 data_length
+= maxlen
;
341 if (att
[i
]->attstorage
!= 'p')
342 has_toastable_attrs
= true;
345 if (!has_toastable_attrs
)
346 return false; /* nothing to toast? */
347 if (maxlength_unknown
)
348 return true; /* any unlimited-length attrs? */
349 tuple_length
= MAXALIGN(offsetof(HeapTupleHeaderData
, t_bits
) +
350 BITMAPLEN(tupdesc
->natts
)) +
351 MAXALIGN(data_length
);
352 return (tuple_length
> TOAST_TUPLE_THRESHOLD
);