diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2000-08-25 18:05:54 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2000-08-25 18:05:54 +0000 |
commit | 3d3ca010aa7d74f9f88ee7f3659c49dee0dcee6b (patch) | |
tree | 53f7897823e6e525a6e83d708948ab01ec152b95 /src/backend/commands/command.c | |
parent | a5da56d320970e76687f3d73e930952d5a8c87f2 (diff) | |
download | postgresql-3d3ca010aa7d74f9f88ee7f3659c49dee0dcee6b.tar.gz postgresql-3d3ca010aa7d74f9f88ee7f3659c49dee0dcee6b.zip |
Avoid creating a TOAST table if we can prove that the maximum tuple
length is < TOAST_TUPLE_THRESHOLD, even with toastable column types
present. For example, CREATE TABLE foo (f1 int, f2 varchar(100))
does not require a toast table, even though varchar is a toastable
type.
Diffstat (limited to 'src/backend/commands/command.c')
-rw-r--r-- | src/backend/commands/command.c | 182 |
1 files changed, 98 insertions, 84 deletions
diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index 13d0ad56497..60405ebd277 100644 --- a/src/backend/commands/command.c +++ b/src/backend/commands/command.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.95 2000/08/21 17:22:32 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.96 2000/08/25 18:05:54 tgl Exp $ * * NOTES * The PerformAddAttribute() code, like most of the relation @@ -19,6 +19,7 @@ */ #include "postgres.h" +#include "access/tuptoaster.h" #include "catalog/catalog.h" #include "catalog/catname.h" #include "catalog/index.h" @@ -52,6 +53,9 @@ #include "access/genam.h" +static bool needs_toast_table(Relation rel); + + /* -------------------------------- * PortalCleanup * -------------------------------- @@ -715,6 +719,7 @@ systable_beginscan(Relation rel, const char *indexRelname, int nkeys, ScanKey en sysscan->scan = heap_beginscan(rel, false, SnapshotNow, nkeys, entry); return (void *) sysscan; } + static void systable_endscan(void *scan) { @@ -731,6 +736,7 @@ systable_endscan(void *scan) heap_endscan(sysscan->scan); pfree(scan); } + static HeapTuple systable_getnext(void *scan) { @@ -780,6 +786,7 @@ find_attribute_walker(Node *node, int attnum) } return expression_tree_walker(node, find_attribute_walker, (void *) attnum); } + static bool find_attribute_in_node(Node *node, int attnum) { @@ -1377,17 +1384,14 @@ AlterTableCreateToastTable(const char *relationName, bool silent) HeapTuple reltup; HeapTupleData classtuple; TupleDesc tupdesc; - Form_pg_attribute *att; Relation class_rel; Buffer buffer; Relation ridescs[Num_pg_class_indices]; Oid toast_relid; Oid toast_idxid; - bool has_toastable_attrs = false; - int i; char toast_relname[NAMEDATALEN + 1]; char toast_idxname[NAMEDATALEN + 1]; - Relation toast_rel; + Relation toast_idxrel; IndexInfo *indexInfo; Oid classObjectId[1]; @@ -1400,15 +1404,23 @@ AlterTableCreateToastTable(const char *relationName, bool silent) #endif /* - * lock the pg_class tuple for update + * Grab an exclusive lock on the target table, which we will NOT + * release until end of transaction. + */ + rel = heap_openr(relationName, AccessExclusiveLock); + myrelid = RelationGetRelid(rel); + + /* + * lock the pg_class tuple for update (is that really needed?) */ + class_rel = heap_openr(RelationRelationName, RowExclusiveLock); + reltup = SearchSysCacheTuple(RELNAME, PointerGetDatum(relationName), 0, 0, 0); - if (!HeapTupleIsValid(reltup)) elog(ERROR, "ALTER TABLE: relation \"%s\" not found", relationName); - class_rel = heap_openr(RelationRelationName, RowExclusiveLock); + classtuple.t_self = reltup->t_self; switch (heap_mark4update(class_rel, &classtuple, &buffer)) { @@ -1422,27 +1434,18 @@ AlterTableCreateToastTable(const char *relationName, bool silent) ReleaseBuffer(buffer); /* - * Grab an exclusive lock on the target table, which we will NOT - * release until end of transaction. - */ - rel = heap_openr(relationName, AccessExclusiveLock); - myrelid = RelationGetRelid(rel); - - /* - * Check if there are any toastable attributes on the table + * XXX is the following check sufficient? At least it would + * allow to create TOAST tables for views. But why not - someone + * can insert into a view, so it shouldn't be impossible to hide + * huge data there :-) */ - tupdesc = rel->rd_att; - att = tupdesc->attrs; - for (i = 0; i < tupdesc->natts; i++) + if (((Form_pg_class) GETSTRUCT(reltup))->relkind != RELKIND_RELATION) { - if (att[i]->attstorage != 'p') - { - has_toastable_attrs = true; - break; - } + elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table", + relationName); } - if (!has_toastable_attrs) + if (((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid != InvalidOid) { if (silent) { @@ -1452,24 +1455,14 @@ AlterTableCreateToastTable(const char *relationName, bool silent) return; } - elog(ERROR, "ALTER TABLE: relation \"%s\" has no toastable attributes", - relationName); - } - + elog(ERROR, "ALTER TABLE: relation \"%s\" already has a toast table", + relationName); + } /* - * XXX is the following check sufficient? At least it would - * allow to create TOAST tables for views. But why not - someone - * can insert into a view, so it shouldn't be impossible to hide - * huge data there :-) + * Check to see whether the table actually needs a TOAST table. */ - if (((Form_pg_class) GETSTRUCT(reltup))->relkind != RELKIND_RELATION) - { - elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table", - relationName); - } - - if (((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid != InvalidOid) + if (! needs_toast_table(rel)) { if (silent) { @@ -1479,9 +1472,9 @@ AlterTableCreateToastTable(const char *relationName, bool silent) return; } - elog(ERROR, "ALTER TABLE: relation \"%s\" already has a toast table", - relationName); - } + elog(ERROR, "ALTER TABLE: relation \"%s\" does not need a toast table", + relationName); + } /* * Create the toast table and its index @@ -1518,8 +1511,9 @@ AlterTableCreateToastTable(const char *relationName, bool silent) * collision, and the toast rel will be destroyed when its master is, * so there's no need to handle the toast rel as temp. */ - heap_create_with_catalog(toast_relname, tupdesc, RELKIND_TOASTVALUE, - false, true); + toast_relid = heap_create_with_catalog(toast_relname, tupdesc, + RELKIND_TOASTVALUE, + false, true); /* make the toast relation visible, else index creation will fail */ CommandCounterIncrement(); @@ -1540,18 +1534,18 @@ AlterTableCreateToastTable(const char *relationName, bool silent) BTREE_AM_OID, classObjectId, false, false, true); - /* make the index visible in this transaction */ - CommandCounterIncrement(); + /* + * Update toast rel's pg_class entry to show that it has an index. + * NOTE this also does CommandCounterIncrement() to make index visible. + */ + setRelhasindexInplace(toast_relid, true, false); /* - * Get the OIDs of the newly created objects + * Get the OID of the newly created index */ - toast_rel = heap_openr(toast_relname, NoLock); - toast_relid = RelationGetRelid(toast_rel); - heap_close(toast_rel, NoLock); - toast_rel = index_openr(toast_idxname); - toast_idxid = RelationGetRelid(toast_rel); - index_close(toast_rel); + toast_idxrel = index_openr(toast_idxname); + toast_idxid = RelationGetRelid(toast_idxrel); + index_close(toast_idxrel); /* * Store the toast table- and index-Oid's in the relation tuple @@ -1570,36 +1564,6 @@ AlterTableCreateToastTable(const char *relationName, bool silent) heap_freetuple(reltup); /* - * Finally update the toast relations pg_class tuple to say - * it has an index. - */ - reltup = SearchSysCacheTuple(RELNAME, PointerGetDatum(toast_relname), - 0, 0, 0); - if (!HeapTupleIsValid(reltup)) - elog(ERROR, "ALTER TABLE: just created toast relation \"%s\" not found", - toast_relname); - classtuple.t_self = reltup->t_self; - switch (heap_mark4update(class_rel, &classtuple, &buffer)) - { - case HeapTupleSelfUpdated: - case HeapTupleMayBeUpdated: - break; - default: - elog(ERROR, "couldn't lock pg_class tuple"); - } - reltup = heap_copytuple(&classtuple); - ReleaseBuffer(buffer); - - ((Form_pg_class) GETSTRUCT(reltup))->relhasindex = true; - heap_update(class_rel, &reltup->t_self, reltup, NULL); - - CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); - CatalogIndexInsert(ridescs, Num_pg_class_indices, class_rel, reltup); - CatalogCloseIndices(Num_pg_class_indices, ridescs); - - heap_freetuple(reltup); - - /* * Close relations and make changes visible */ heap_close(class_rel, NoLock); @@ -1608,6 +1572,56 @@ AlterTableCreateToastTable(const char *relationName, bool silent) CommandCounterIncrement(); } +/* + * Check to see whether the table needs a TOAST table. It does only if + * (1) there are any toastable attributes, and (2) the maximum length + * of a tuple could exceed TOAST_TUPLE_THRESHOLD. (We don't want to + * create a toast table for something like "f1 varchar(20)".) + */ +static bool +needs_toast_table(Relation rel) +{ + int32 data_length = 0; + bool maxlength_unknown = false; + bool has_toastable_attrs = false; + TupleDesc tupdesc; + Form_pg_attribute *att; + int32 tuple_length; + int i; + + tupdesc = rel->rd_att; + att = tupdesc->attrs; + + for (i = 0; i < tupdesc->natts; i++) + { + data_length = att_align(data_length, att[i]->attlen, att[i]->attalign); + if (att[i]->attlen >= 0) + { + /* Fixed-length types are never toastable */ + data_length += att[i]->attlen; + } + else + { + int32 maxlen = type_maximum_size(att[i]->atttypid, + att[i]->atttypmod); + + if (maxlen < 0) + maxlength_unknown = true; + else + data_length += maxlen; + if (att[i]->attstorage != 'p') + has_toastable_attrs = true; + } + } + if (!has_toastable_attrs) + return false; /* nothing to toast? */ + if (maxlength_unknown) + return true; /* any unlimited-length attrs? */ + tuple_length = MAXALIGN(offsetof(HeapTupleHeaderData, t_bits) + + BITMAPLEN(tupdesc->natts)) + + MAXALIGN(data_length); + return (tuple_length > TOAST_TUPLE_THRESHOLD); +} /* |