mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-30 19:00:29 +08:00
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.
This commit is contained in:
parent
a5da56d320
commit
3d3ca010aa
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* The PerformAddAttribute() code, like most of the relation
|
* The PerformAddAttribute() code, like most of the relation
|
||||||
@ -19,6 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/tuptoaster.h"
|
||||||
#include "catalog/catalog.h"
|
#include "catalog/catalog.h"
|
||||||
#include "catalog/catname.h"
|
#include "catalog/catname.h"
|
||||||
#include "catalog/index.h"
|
#include "catalog/index.h"
|
||||||
@ -52,6 +53,9 @@
|
|||||||
#include "access/genam.h"
|
#include "access/genam.h"
|
||||||
|
|
||||||
|
|
||||||
|
static bool needs_toast_table(Relation rel);
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------------
|
/* --------------------------------
|
||||||
* PortalCleanup
|
* 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);
|
sysscan->scan = heap_beginscan(rel, false, SnapshotNow, nkeys, entry);
|
||||||
return (void *) sysscan;
|
return (void *) sysscan;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
systable_endscan(void *scan)
|
systable_endscan(void *scan)
|
||||||
{
|
{
|
||||||
@ -731,6 +736,7 @@ systable_endscan(void *scan)
|
|||||||
heap_endscan(sysscan->scan);
|
heap_endscan(sysscan->scan);
|
||||||
pfree(scan);
|
pfree(scan);
|
||||||
}
|
}
|
||||||
|
|
||||||
static HeapTuple
|
static HeapTuple
|
||||||
systable_getnext(void *scan)
|
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);
|
return expression_tree_walker(node, find_attribute_walker, (void *) attnum);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
find_attribute_in_node(Node *node, int attnum)
|
find_attribute_in_node(Node *node, int attnum)
|
||||||
{
|
{
|
||||||
@ -1377,17 +1384,14 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
|
|||||||
HeapTuple reltup;
|
HeapTuple reltup;
|
||||||
HeapTupleData classtuple;
|
HeapTupleData classtuple;
|
||||||
TupleDesc tupdesc;
|
TupleDesc tupdesc;
|
||||||
Form_pg_attribute *att;
|
|
||||||
Relation class_rel;
|
Relation class_rel;
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
Relation ridescs[Num_pg_class_indices];
|
Relation ridescs[Num_pg_class_indices];
|
||||||
Oid toast_relid;
|
Oid toast_relid;
|
||||||
Oid toast_idxid;
|
Oid toast_idxid;
|
||||||
bool has_toastable_attrs = false;
|
|
||||||
int i;
|
|
||||||
char toast_relname[NAMEDATALEN + 1];
|
char toast_relname[NAMEDATALEN + 1];
|
||||||
char toast_idxname[NAMEDATALEN + 1];
|
char toast_idxname[NAMEDATALEN + 1];
|
||||||
Relation toast_rel;
|
Relation toast_idxrel;
|
||||||
IndexInfo *indexInfo;
|
IndexInfo *indexInfo;
|
||||||
Oid classObjectId[1];
|
Oid classObjectId[1];
|
||||||
|
|
||||||
@ -1400,15 +1404,23 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
|
|||||||
#endif
|
#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),
|
reltup = SearchSysCacheTuple(RELNAME, PointerGetDatum(relationName),
|
||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
|
|
||||||
if (!HeapTupleIsValid(reltup))
|
if (!HeapTupleIsValid(reltup))
|
||||||
elog(ERROR, "ALTER TABLE: relation \"%s\" not found",
|
elog(ERROR, "ALTER TABLE: relation \"%s\" not found",
|
||||||
relationName);
|
relationName);
|
||||||
class_rel = heap_openr(RelationRelationName, RowExclusiveLock);
|
|
||||||
classtuple.t_self = reltup->t_self;
|
classtuple.t_self = reltup->t_self;
|
||||||
switch (heap_mark4update(class_rel, &classtuple, &buffer))
|
switch (heap_mark4update(class_rel, &classtuple, &buffer))
|
||||||
{
|
{
|
||||||
@ -1421,42 +1433,6 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
|
|||||||
reltup = heap_copytuple(&classtuple);
|
reltup = heap_copytuple(&classtuple);
|
||||||
ReleaseBuffer(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
|
|
||||||
*/
|
|
||||||
tupdesc = rel->rd_att;
|
|
||||||
att = tupdesc->attrs;
|
|
||||||
for (i = 0; i < tupdesc->natts; i++)
|
|
||||||
{
|
|
||||||
if (att[i]->attstorage != 'p')
|
|
||||||
{
|
|
||||||
has_toastable_attrs = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!has_toastable_attrs)
|
|
||||||
{
|
|
||||||
if (silent)
|
|
||||||
{
|
|
||||||
heap_close(rel, NoLock);
|
|
||||||
heap_close(class_rel, NoLock);
|
|
||||||
heap_freetuple(reltup);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
elog(ERROR, "ALTER TABLE: relation \"%s\" has no toastable attributes",
|
|
||||||
relationName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX is the following check sufficient? At least it would
|
* XXX is the following check sufficient? At least it would
|
||||||
* allow to create TOAST tables for views. But why not - someone
|
* allow to create TOAST tables for views. But why not - someone
|
||||||
@ -1480,9 +1456,26 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
elog(ERROR, "ALTER TABLE: relation \"%s\" already has a toast table",
|
elog(ERROR, "ALTER TABLE: relation \"%s\" already has a toast table",
|
||||||
relationName);
|
relationName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check to see whether the table actually needs a TOAST table.
|
||||||
|
*/
|
||||||
|
if (! needs_toast_table(rel))
|
||||||
|
{
|
||||||
|
if (silent)
|
||||||
|
{
|
||||||
|
heap_close(rel, NoLock);
|
||||||
|
heap_close(class_rel, NoLock);
|
||||||
|
heap_freetuple(reltup);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
elog(ERROR, "ALTER TABLE: relation \"%s\" does not need a toast table",
|
||||||
|
relationName);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create the toast table and its index
|
* 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,
|
* collision, and the toast rel will be destroyed when its master is,
|
||||||
* so there's no need to handle the toast rel as temp.
|
* so there's no need to handle the toast rel as temp.
|
||||||
*/
|
*/
|
||||||
heap_create_with_catalog(toast_relname, tupdesc, RELKIND_TOASTVALUE,
|
toast_relid = heap_create_with_catalog(toast_relname, tupdesc,
|
||||||
false, true);
|
RELKIND_TOASTVALUE,
|
||||||
|
false, true);
|
||||||
|
|
||||||
/* make the toast relation visible, else index creation will fail */
|
/* make the toast relation visible, else index creation will fail */
|
||||||
CommandCounterIncrement();
|
CommandCounterIncrement();
|
||||||
@ -1540,18 +1534,18 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
|
|||||||
BTREE_AM_OID, classObjectId,
|
BTREE_AM_OID, classObjectId,
|
||||||
false, false, true);
|
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_idxrel = index_openr(toast_idxname);
|
||||||
toast_relid = RelationGetRelid(toast_rel);
|
toast_idxid = RelationGetRelid(toast_idxrel);
|
||||||
heap_close(toast_rel, NoLock);
|
index_close(toast_idxrel);
|
||||||
toast_rel = index_openr(toast_idxname);
|
|
||||||
toast_idxid = RelationGetRelid(toast_rel);
|
|
||||||
index_close(toast_rel);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Store the toast table- and index-Oid's in the relation tuple
|
* Store the toast table- and index-Oid's in the relation tuple
|
||||||
@ -1569,36 +1563,6 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
|
|||||||
|
|
||||||
heap_freetuple(reltup);
|
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
|
* Close relations and make changes visible
|
||||||
*/
|
*/
|
||||||
@ -1608,6 +1572,56 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
|
|||||||
CommandCounterIncrement();
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -20,6 +20,7 @@
|
|||||||
#include "fmgr.h"
|
#include "fmgr.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/numeric.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
#define MAX_INT32_LEN 11
|
#define MAX_INT32_LEN 11
|
||||||
@ -214,6 +215,53 @@ format_type_internal(Oid type_oid, int32 typemod)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
* oidvectortypes - converts a vector of type OIDs to "typname" list
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: builtins.h,v 1.134 2000/08/24 03:29:14 tgl Exp $
|
* $Id: builtins.h,v 1.135 2000/08/25 18:05:53 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -583,5 +583,6 @@ extern Datum PG_char_to_encoding(PG_FUNCTION_ARGS);
|
|||||||
/* format_type.c */
|
/* format_type.c */
|
||||||
extern Datum format_type(PG_FUNCTION_ARGS);
|
extern Datum format_type(PG_FUNCTION_ARGS);
|
||||||
extern Datum oidvectortypes(PG_FUNCTION_ARGS);
|
extern Datum oidvectortypes(PG_FUNCTION_ARGS);
|
||||||
|
extern int32 type_maximum_size(Oid type_oid, int32 typemod);
|
||||||
|
|
||||||
#endif /* BUILTINS_H */
|
#endif /* BUILTINS_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user