Skip to content

Commit 3d3ca01

Browse files
committed
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.
1 parent a5da56d commit 3d3ca01

File tree

3 files changed

+149
-86
lines changed

3 files changed

+149
-86
lines changed

src/backend/commands/command.c

Lines changed: 98 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.95 2000/08/21 17:22:32 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.96 2000/08/25 18:05:54 tgl Exp $
1212
*
1313
* NOTES
1414
* The PerformAddAttribute() code, like most of the relation
@@ -19,6 +19,7 @@
1919
*/
2020
#include "postgres.h"
2121

22+
#include "access/tuptoaster.h"
2223
#include "catalog/catalog.h"
2324
#include "catalog/catname.h"
2425
#include "catalog/index.h"
@@ -52,6 +53,9 @@
5253
#include "access/genam.h"
5354

5455

56+
static bool needs_toast_table(Relation rel);
57+
58+
5559
/* --------------------------------
5660
* PortalCleanup
5761
* --------------------------------
@@ -715,6 +719,7 @@ systable_beginscan(Relation rel, const char *indexRelname, int nkeys, ScanKey en
715719
sysscan->scan = heap_beginscan(rel, false, SnapshotNow, nkeys, entry);
716720
return (void *) sysscan;
717721
}
722+
718723
static void
719724
systable_endscan(void *scan)
720725
{
@@ -731,6 +736,7 @@ systable_endscan(void *scan)
731736
heap_endscan(sysscan->scan);
732737
pfree(scan);
733738
}
739+
734740
static HeapTuple
735741
systable_getnext(void *scan)
736742
{
@@ -780,6 +786,7 @@ find_attribute_walker(Node *node, int attnum)
780786
}
781787
return expression_tree_walker(node, find_attribute_walker, (void *) attnum);
782788
}
789+
783790
static bool
784791
find_attribute_in_node(Node *node, int attnum)
785792
{
@@ -1377,17 +1384,14 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
13771384
HeapTuple reltup;
13781385
HeapTupleData classtuple;
13791386
TupleDesc tupdesc;
1380-
Form_pg_attribute *att;
13811387
Relation class_rel;
13821388
Buffer buffer;
13831389
Relation ridescs[Num_pg_class_indices];
13841390
Oid toast_relid;
13851391
Oid toast_idxid;
1386-
bool has_toastable_attrs = false;
1387-
int i;
13881392
char toast_relname[NAMEDATALEN + 1];
13891393
char toast_idxname[NAMEDATALEN + 1];
1390-
Relation toast_rel;
1394+
Relation toast_idxrel;
13911395
IndexInfo *indexInfo;
13921396
Oid classObjectId[1];
13931397

@@ -1400,15 +1404,23 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
14001404
#endif
14011405

14021406
/*
1403-
* lock the pg_class tuple for update
1407+
* Grab an exclusive lock on the target table, which we will NOT
1408+
* release until end of transaction.
1409+
*/
1410+
rel = heap_openr(relationName, AccessExclusiveLock);
1411+
myrelid = RelationGetRelid(rel);
1412+
1413+
/*
1414+
* lock the pg_class tuple for update (is that really needed?)
14041415
*/
1416+
class_rel = heap_openr(RelationRelationName, RowExclusiveLock);
1417+
14051418
reltup = SearchSysCacheTuple(RELNAME, PointerGetDatum(relationName),
14061419
0, 0, 0);
1407-
14081420
if (!HeapTupleIsValid(reltup))
14091421
elog(ERROR, "ALTER TABLE: relation \"%s\" not found",
14101422
relationName);
1411-
class_rel = heap_openr(RelationRelationName, RowExclusiveLock);
1423+
14121424
classtuple.t_self = reltup->t_self;
14131425
switch (heap_mark4update(class_rel, &classtuple, &buffer))
14141426
{
@@ -1422,27 +1434,18 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
14221434
ReleaseBuffer(buffer);
14231435

14241436
/*
1425-
* Grab an exclusive lock on the target table, which we will NOT
1426-
* release until end of transaction.
1427-
*/
1428-
rel = heap_openr(relationName, AccessExclusiveLock);
1429-
myrelid = RelationGetRelid(rel);
1430-
1431-
/*
1432-
* Check if there are any toastable attributes on the table
1437+
* XXX is the following check sufficient? At least it would
1438+
* allow to create TOAST tables for views. But why not - someone
1439+
* can insert into a view, so it shouldn't be impossible to hide
1440+
* huge data there :-)
14331441
*/
1434-
tupdesc = rel->rd_att;
1435-
att = tupdesc->attrs;
1436-
for (i = 0; i < tupdesc->natts; i++)
1442+
if (((Form_pg_class) GETSTRUCT(reltup))->relkind != RELKIND_RELATION)
14371443
{
1438-
if (att[i]->attstorage != 'p')
1439-
{
1440-
has_toastable_attrs = true;
1441-
break;
1442-
}
1444+
elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
1445+
relationName);
14431446
}
14441447

1445-
if (!has_toastable_attrs)
1448+
if (((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid != InvalidOid)
14461449
{
14471450
if (silent)
14481451
{
@@ -1452,24 +1455,14 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
14521455
return;
14531456
}
14541457

1455-
elog(ERROR, "ALTER TABLE: relation \"%s\" has no toastable attributes",
1456-
relationName);
1457-
}
1458-
1458+
elog(ERROR, "ALTER TABLE: relation \"%s\" already has a toast table",
1459+
relationName);
1460+
}
14591461

14601462
/*
1461-
* XXX is the following check sufficient? At least it would
1462-
* allow to create TOAST tables for views. But why not - someone
1463-
* can insert into a view, so it shouldn't be impossible to hide
1464-
* huge data there :-)
1463+
* Check to see whether the table actually needs a TOAST table.
14651464
*/
1466-
if (((Form_pg_class) GETSTRUCT(reltup))->relkind != RELKIND_RELATION)
1467-
{
1468-
elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
1469-
relationName);
1470-
}
1471-
1472-
if (((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid != InvalidOid)
1465+
if (! needs_toast_table(rel))
14731466
{
14741467
if (silent)
14751468
{
@@ -1479,9 +1472,9 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
14791472
return;
14801473
}
14811474

1482-
elog(ERROR, "ALTER TABLE: relation \"%s\" already has a toast table",
1483-
relationName);
1484-
}
1475+
elog(ERROR, "ALTER TABLE: relation \"%s\" does not need a toast table",
1476+
relationName);
1477+
}
14851478

14861479
/*
14871480
* Create the toast table and its index
@@ -1518,8 +1511,9 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
15181511
* collision, and the toast rel will be destroyed when its master is,
15191512
* so there's no need to handle the toast rel as temp.
15201513
*/
1521-
heap_create_with_catalog(toast_relname, tupdesc, RELKIND_TOASTVALUE,
1522-
false, true);
1514+
toast_relid = heap_create_with_catalog(toast_relname, tupdesc,
1515+
RELKIND_TOASTVALUE,
1516+
false, true);
15231517

15241518
/* make the toast relation visible, else index creation will fail */
15251519
CommandCounterIncrement();
@@ -1540,18 +1534,18 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
15401534
BTREE_AM_OID, classObjectId,
15411535
false, false, true);
15421536

1543-
/* make the index visible in this transaction */
1544-
CommandCounterIncrement();
1537+
/*
1538+
* Update toast rel's pg_class entry to show that it has an index.
1539+
* NOTE this also does CommandCounterIncrement() to make index visible.
1540+
*/
1541+
setRelhasindexInplace(toast_relid, true, false);
15451542

15461543
/*
1547-
* Get the OIDs of the newly created objects
1544+
* Get the OID of the newly created index
15481545
*/
1549-
toast_rel = heap_openr(toast_relname, NoLock);
1550-
toast_relid = RelationGetRelid(toast_rel);
1551-
heap_close(toast_rel, NoLock);
1552-
toast_rel = index_openr(toast_idxname);
1553-
toast_idxid = RelationGetRelid(toast_rel);
1554-
index_close(toast_rel);
1546+
toast_idxrel = index_openr(toast_idxname);
1547+
toast_idxid = RelationGetRelid(toast_idxrel);
1548+
index_close(toast_idxrel);
15551549

15561550
/*
15571551
* Store the toast table- and index-Oid's in the relation tuple
@@ -1569,36 +1563,6 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
15691563

15701564
heap_freetuple(reltup);
15711565

1572-
/*
1573-
* Finally update the toast relations pg_class tuple to say
1574-
* it has an index.
1575-
*/
1576-
reltup = SearchSysCacheTuple(RELNAME, PointerGetDatum(toast_relname),
1577-
0, 0, 0);
1578-
if (!HeapTupleIsValid(reltup))
1579-
elog(ERROR, "ALTER TABLE: just created toast relation \"%s\" not found",
1580-
toast_relname);
1581-
classtuple.t_self = reltup->t_self;
1582-
switch (heap_mark4update(class_rel, &classtuple, &buffer))
1583-
{
1584-
case HeapTupleSelfUpdated:
1585-
case HeapTupleMayBeUpdated:
1586-
break;
1587-
default:
1588-
elog(ERROR, "couldn't lock pg_class tuple");
1589-
}
1590-
reltup = heap_copytuple(&classtuple);
1591-
ReleaseBuffer(buffer);
1592-
1593-
((Form_pg_class) GETSTRUCT(reltup))->relhasindex = true;
1594-
heap_update(class_rel, &reltup->t_self, reltup, NULL);
1595-
1596-
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
1597-
CatalogIndexInsert(ridescs, Num_pg_class_indices, class_rel, reltup);
1598-
CatalogCloseIndices(Num_pg_class_indices, ridescs);
1599-
1600-
heap_freetuple(reltup);
1601-
16021566
/*
16031567
* Close relations and make changes visible
16041568
*/
@@ -1608,6 +1572,56 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
16081572
CommandCounterIncrement();
16091573
}
16101574

1575+
/*
1576+
* Check to see whether the table needs a TOAST table. It does only if
1577+
* (1) there are any toastable attributes, and (2) the maximum length
1578+
* of a tuple could exceed TOAST_TUPLE_THRESHOLD. (We don't want to
1579+
* create a toast table for something like "f1 varchar(20)".)
1580+
*/
1581+
static bool
1582+
needs_toast_table(Relation rel)
1583+
{
1584+
int32 data_length = 0;
1585+
bool maxlength_unknown = false;
1586+
bool has_toastable_attrs = false;
1587+
TupleDesc tupdesc;
1588+
Form_pg_attribute *att;
1589+
int32 tuple_length;
1590+
int i;
1591+
1592+
tupdesc = rel->rd_att;
1593+
att = tupdesc->attrs;
1594+
1595+
for (i = 0; i < tupdesc->natts; i++)
1596+
{
1597+
data_length = att_align(data_length, att[i]->attlen, att[i]->attalign);
1598+
if (att[i]->attlen >= 0)
1599+
{
1600+
/* Fixed-length types are never toastable */
1601+
data_length += att[i]->attlen;
1602+
}
1603+
else
1604+
{
1605+
int32 maxlen = type_maximum_size(att[i]->atttypid,
1606+
att[i]->atttypmod);
1607+
1608+
if (maxlen < 0)
1609+
maxlength_unknown = true;
1610+
else
1611+
data_length += maxlen;
1612+
if (att[i]->attstorage != 'p')
1613+
has_toastable_attrs = true;
1614+
}
1615+
}
1616+
if (!has_toastable_attrs)
1617+
return false; /* nothing to toast? */
1618+
if (maxlength_unknown)
1619+
return true; /* any unlimited-length attrs? */
1620+
tuple_length = MAXALIGN(offsetof(HeapTupleHeaderData, t_bits) +
1621+
BITMAPLEN(tupdesc->natts)) +
1622+
MAXALIGN(data_length);
1623+
return (tuple_length > TOAST_TUPLE_THRESHOLD);
1624+
}
16111625

16121626

16131627
/*

src/backend/utils/adt/format_type.c

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.3 2000/08/21 18:23:18 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.4 2000/08/25 18:05:54 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -20,6 +20,7 @@
2020
#include "fmgr.h"
2121
#include "catalog/pg_type.h"
2222
#include "utils/builtins.h"
23+
#include "utils/numeric.h"
2324
#include "utils/syscache.h"
2425

2526
#define MAX_INT32_LEN 11
@@ -214,6 +215,53 @@ format_type_internal(Oid type_oid, int32 typemod)
214215
}
215216

216217

218+
/*
219+
* type_maximum_size --- determine maximum length of a varlena column
220+
*
221+
* If the max length is indeterminate, return -1. In particular, we return
222+
* -1 for any type not known to this routine. We assume the caller has
223+
* already determined that the type is a varlena type, so it's not
224+
* necessary to look up the type's pg_type tuple here.
225+
*
226+
* This may appear unrelated to format_type(), but in fact the two routines
227+
* share knowledge of the encoding of typmod for different types, so it's
228+
* convenient to keep them together.
229+
*/
230+
int32
231+
type_maximum_size(Oid type_oid, int32 typemod)
232+
{
233+
if (typemod <= 0)
234+
return -1;
235+
236+
switch (type_oid)
237+
{
238+
case BPCHAROID:
239+
case VARCHAROID:
240+
/* typemod includes varlena header */
241+
return typemod;
242+
243+
case NUMERICOID:
244+
/* precision (ie, max # of digits) is in upper bits of typmod */
245+
if (typemod > VARHDRSZ)
246+
{
247+
int precision = ((typemod - VARHDRSZ) >> 16) & 0xffff;
248+
249+
/* Numeric stores 2 decimal digits/byte, plus header */
250+
return (precision + 1) / 2 + NUMERIC_HDRSZ;
251+
}
252+
break;
253+
254+
case VARBITOID:
255+
case ZPBITOID:
256+
/* typemod is the (max) number of bits */
257+
return (typemod + (BITSPERBYTE-1)) / BITSPERBYTE
258+
+ 2 * sizeof(int32);
259+
}
260+
261+
/* Unknown type, or unlimited-length type such as 'text' */
262+
return -1;
263+
}
264+
217265

218266
/*
219267
* oidvectortypes - converts a vector of type OIDs to "typname" list

src/include/utils/builtins.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $Id: builtins.h,v 1.134 2000/08/24 03:29:14 tgl Exp $
10+
* $Id: builtins.h,v 1.135 2000/08/25 18:05:53 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -583,5 +583,6 @@ extern Datum PG_char_to_encoding(PG_FUNCTION_ARGS);
583583
/* format_type.c */
584584
extern Datum format_type(PG_FUNCTION_ARGS);
585585
extern Datum oidvectortypes(PG_FUNCTION_ARGS);
586+
extern int32 type_maximum_size(Oid type_oid, int32 typemod);
586587

587588
#endif /* BUILTINS_H */

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy