*
*
* 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
*/
#include "postgres.h"
+#include "access/tuptoaster.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
#include "catalog/index.h"
#include "access/genam.h"
+static bool needs_toast_table(Relation rel);
+
+
/* --------------------------------
* PortalCleanup
* --------------------------------
sysscan->scan = heap_beginscan(rel, false, SnapshotNow, nkeys, entry);
return (void *) sysscan;
}
+
static void
systable_endscan(void *scan)
{
heap_endscan(sysscan->scan);
pfree(scan);
}
+
static HeapTuple
systable_getnext(void *scan)
{
}
return expression_tree_walker(node, find_attribute_walker, (void *) attnum);
}
+
static bool
find_attribute_in_node(Node *node, int attnum)
{
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];
#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))
{
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)
{
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)
{
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
* 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();
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
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
*/
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);
+}
/*
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.3 2000/08/21 18:23:18 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.4 2000/08/25 18:05:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "fmgr.h"
#include "catalog/pg_type.h"
#include "utils/builtins.h"
+#include "utils/numeric.h"
#include "utils/syscache.h"
#define MAX_INT32_LEN 11
}
+/*
+ * type_maximum_size --- determine maximum length of a varlena column
+ *
+ * If the max length is indeterminate, return -1. In particular, we return
+ * -1 for any type not known to this routine. We assume the caller has
+ * already determined that the type is a varlena type, so it's not
+ * necessary to look up the type's pg_type tuple here.
+ *
+ * This may appear unrelated to format_type(), but in fact the two routines
+ * share knowledge of the encoding of typmod for different types, so it's
+ * convenient to keep them together.
+ */
+int32
+type_maximum_size(Oid type_oid, int32 typemod)
+{
+ if (typemod <= 0)
+ return -1;
+
+ switch (type_oid)
+ {
+ case BPCHAROID:
+ case VARCHAROID:
+ /* typemod includes varlena header */
+ return typemod;
+
+ case NUMERICOID:
+ /* precision (ie, max # of digits) is in upper bits of typmod */
+ if (typemod > VARHDRSZ)
+ {
+ int precision = ((typemod - VARHDRSZ) >> 16) & 0xffff;
+
+ /* Numeric stores 2 decimal digits/byte, plus header */
+ return (precision + 1) / 2 + NUMERIC_HDRSZ;
+ }
+ break;
+
+ case VARBITOID:
+ case ZPBITOID:
+ /* typemod is the (max) number of bits */
+ return (typemod + (BITSPERBYTE-1)) / BITSPERBYTE
+ + 2 * sizeof(int32);
+ }
+
+ /* Unknown type, or unlimited-length type such as 'text' */
+ return -1;
+}
+
/*
* oidvectortypes - converts a vector of type OIDs to "typname" list