Skip to content

Commit d76e1cf

Browse files
committed
Implement CREATE TABLE .. ( STORAGE .. )
1 parent bf9a55c commit d76e1cf

File tree

6 files changed

+101
-28
lines changed

6 files changed

+101
-28
lines changed

doc/src/sgml/ref/create_table.sgml

+23-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ PostgreSQL documentation
2222
<refsynopsisdiv>
2323
<synopsis>
2424
CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] <replaceable class="parameter">table_name</replaceable> ( [
25-
{ <replaceable class="parameter">column_name</replaceable> <replaceable class="parameter">data_type</replaceable> [ COMPRESSION <replaceable>compression_method</replaceable> ] [ COLLATE <replaceable>collation</replaceable> ] [ <replaceable class="parameter">column_constraint</replaceable> [ ... ] ]
25+
{ <replaceable class="parameter">column_name</replaceable> <replaceable class="parameter">data_type</replaceable> [ STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN } ] [ COMPRESSION <replaceable>compression_method</replaceable> ] [ COLLATE <replaceable>collation</replaceable> ] [ <replaceable class="parameter">column_constraint</replaceable> [ ... ] ]
2626
| <replaceable>table_constraint</replaceable>
2727
| LIKE <replaceable>source_table</replaceable> [ <replaceable>like_option</replaceable> ... ] }
2828
[, ... ]
@@ -292,6 +292,28 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
292292
</listitem>
293293
</varlistentry>
294294

295+
<varlistentry>
296+
<term> <literal>SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }</literal></term>
297+
<listitem>
298+
<para>
299+
This form sets the storage mode for a column. This controls whether this
300+
column is held inline or in a secondary <acronym>TOAST</acronym> table, and
301+
whether the data
302+
should be compressed or not. <literal>PLAIN</literal> must be used
303+
for fixed-length values such as <type>integer</type> and is
304+
inline, uncompressed. <literal>MAIN</literal> is for inline,
305+
compressible data. <literal>EXTERNAL</literal> is for external,
306+
uncompressed data, and <literal>EXTENDED</literal> is for external,
307+
compressed data. <literal>EXTENDED</literal> is the default for most
308+
data types that support non-<literal>PLAIN</literal> storage.
309+
Use of <literal>EXTERNAL</literal> will make substring operations on
310+
very large <type>text</type> and <type>bytea</type> values run faster,
311+
at the penalty of increased storage space.
312+
See <xref linkend="storage-toast"/> for more information.
313+
</para>
314+
</listitem>
315+
</varlistentry>
316+
295317
<varlistentry>
296318
<term><literal>COMPRESSION <replaceable class="parameter">compression_method</replaceable></literal></term>
297319
<listitem>

src/backend/commands/tablecmds.c

+56-19
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,7 @@ static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
607607
static List *GetParentedForeignKeyRefs(Relation partition);
608608
static void ATDetachCheckNoForeignKeyRefs(Relation partition);
609609
static char GetAttributeCompression(Oid atttypid, char *compression);
610+
static char GetAttributeStorage(const char *storagemode);
610611

611612

612613
/* ----------------------------------------------------------------
@@ -905,6 +906,22 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
905906
if (colDef->compression)
906907
attr->attcompression = GetAttributeCompression(attr->atttypid,
907908
colDef->compression);
909+
910+
if (colDef->storage_name)
911+
{
912+
attr->attstorage = GetAttributeStorage(colDef->storage_name);
913+
/*
914+
* safety check: do not allow toasted storage modes unless column datatype
915+
* is TOAST-aware.
916+
*/
917+
if (!(attr->attstorage == TYPSTORAGE_PLAIN ||
918+
TypeIsToastable(attr->atttypid)))
919+
ereport(ERROR,
920+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
921+
errmsg("column data type %s can only have storage PLAIN",
922+
format_type_be(attr->atttypid))));
923+
}
924+
908925
}
909926

910927
/*
@@ -6751,7 +6768,23 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
67516768
attribute.atttypmod = typmod;
67526769
attribute.attbyval = tform->typbyval;
67536770
attribute.attalign = tform->typalign;
6754-
attribute.attstorage = tform->typstorage;
6771+
if (colDef->storage_name)
6772+
{
6773+
attribute.attstorage = GetAttributeStorage(colDef->storage_name);
6774+
/*
6775+
* safety check: do not allow toasted storage modes unless column datatype
6776+
* is TOAST-aware.
6777+
*/
6778+
if (!(attribute.attstorage == TYPSTORAGE_PLAIN ||
6779+
TypeIsToastable(attribute.atttypid)))
6780+
ereport(ERROR,
6781+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6782+
errmsg("column data type %s can only have storage PLAIN",
6783+
format_type_be(attribute.atttypid))));
6784+
}
6785+
else
6786+
attribute.attstorage = tform->typstorage;
6787+
67556788
attribute.attcompression = GetAttributeCompression(typeOid,
67566789
colDef->compression);
67576790
attribute.attnotnull = colDef->is_not_null;
@@ -8220,7 +8253,6 @@ SetIndexStorageProperties(Relation rel, Relation attrelation,
82208253
static ObjectAddress
82218254
ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
82228255
{
8223-
char *storagemode;
82248256
char newstorage;
82258257
Relation attrelation;
82268258
HeapTuple tuple;
@@ -8229,24 +8261,8 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc
82298261
ObjectAddress address;
82308262

82318263
Assert(IsA(newValue, String));
8232-
storagemode = strVal(newValue);
82338264

8234-
if (pg_strcasecmp(storagemode, "plain") == 0)
8235-
newstorage = TYPSTORAGE_PLAIN;
8236-
else if (pg_strcasecmp(storagemode, "external") == 0)
8237-
newstorage = TYPSTORAGE_EXTERNAL;
8238-
else if (pg_strcasecmp(storagemode, "extended") == 0)
8239-
newstorage = TYPSTORAGE_EXTENDED;
8240-
else if (pg_strcasecmp(storagemode, "main") == 0)
8241-
newstorage = TYPSTORAGE_MAIN;
8242-
else
8243-
{
8244-
ereport(ERROR,
8245-
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8246-
errmsg("invalid storage type \"%s\"",
8247-
storagemode)));
8248-
newstorage = 0; /* keep compiler quiet */
8249-
}
8265+
newstorage = GetAttributeStorage(strVal(newValue));
82508266

82518267
attrelation = table_open(AttributeRelationId, RowExclusiveLock);
82528268

@@ -18959,3 +18975,24 @@ GetAttributeCompression(Oid atttypid, char *compression)
1895918975

1896018976
return cmethod;
1896118977
}
18978+
18979+
static char
18980+
GetAttributeStorage(const char *storagemode)
18981+
{
18982+
if (pg_strcasecmp(storagemode, "plain") == 0)
18983+
return TYPSTORAGE_PLAIN;
18984+
else if (pg_strcasecmp(storagemode, "external") == 0)
18985+
return TYPSTORAGE_EXTERNAL;
18986+
else if (pg_strcasecmp(storagemode, "extended") == 0)
18987+
return TYPSTORAGE_EXTENDED;
18988+
else if (pg_strcasecmp(storagemode, "main") == 0)
18989+
return TYPSTORAGE_MAIN;
18990+
else
18991+
{
18992+
ereport(ERROR,
18993+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
18994+
errmsg("invalid storage type \"%s\"",
18995+
storagemode)));
18996+
return 0; /* keep compiler quiet */
18997+
}
18998+
}

src/backend/parser/gram.y

+12-6
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
582582

583583
%type <node> TableConstraint TableLikeClause
584584
%type <ival> TableLikeOptionList TableLikeOption
585-
%type <str> column_compression opt_column_compression
585+
%type <str> column_compression opt_column_compression opt_column_storage
586586
%type <list> ColQualList
587587
%type <node> ColConstraint ColConstraintElem ConstraintAttr
588588
%type <ival> key_match
@@ -2317,7 +2317,7 @@ alter_table_cmd:
23172317
$$ = (Node *)n;
23182318
}
23192319
/* ALTER TABLE <name> ALTER [COLUMN] <colname> SET STORAGE <storagemode> */
2320-
| ALTER opt_column ColId SET STORAGE ColId
2320+
| ALTER opt_column ColId SET STORAGE name
23212321
{
23222322
AlterTableCmd *n = makeNode(AlterTableCmd);
23232323
n->subtype = AT_SetStorage;
@@ -3493,12 +3493,13 @@ TypedTableElement:
34933493
| TableConstraint { $$ = $1; }
34943494
;
34953495

3496-
columnDef: ColId Typename opt_column_compression create_generic_options ColQualList
3496+
columnDef: ColId Typename opt_column_storage opt_column_compression create_generic_options ColQualList
34973497
{
34983498
ColumnDef *n = makeNode(ColumnDef);
34993499
n->colname = $1;
35003500
n->typeName = $2;
3501-
n->compression = $3;
3501+
n->storage_name = $3;
3502+
n->compression = $4;
35023503
n->inhcount = 0;
35033504
n->is_local = true;
35043505
n->is_not_null = false;
@@ -3507,8 +3508,8 @@ columnDef: ColId Typename opt_column_compression create_generic_options ColQualL
35073508
n->raw_default = NULL;
35083509
n->cooked_default = NULL;
35093510
n->collOid = InvalidOid;
3510-
n->fdwoptions = $4;
3511-
SplitColQualList($5, &n->constraints, &n->collClause,
3511+
n->fdwoptions = $5;
3512+
SplitColQualList($6, &n->constraints, &n->collClause,
35123513
yyscanner);
35133514
n->location = @1;
35143515
$$ = (Node *)n;
@@ -3563,6 +3564,11 @@ opt_column_compression:
35633564
| /*EMPTY*/ { $$ = NULL; }
35643565
;
35653566

3567+
opt_column_storage:
3568+
STORAGE ColId { $$ = $2; }
3569+
| /*EMPTY*/ { $$ = NULL; }
3570+
;
3571+
35663572
ColQualList:
35673573
ColQualList ColConstraint { $$ = lappend($1, $2); }
35683574
| /*EMPTY*/ { $$ = NIL; }

src/include/nodes/parsenodes.h

+1
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,7 @@ typedef struct ColumnDef
675675
bool is_not_null; /* NOT NULL constraint specified? */
676676
bool is_from_type; /* column definition came from table type */
677677
char storage; /* attstorage setting, or 0 for default */
678+
char *storage_name; /* attstorage setting name or NULL for default*/
678679
Node *raw_default; /* default value (untransformed parse tree) */
679680
Node *cooked_default; /* default value (transformed expr tree) */
680681
char identity; /* attidentity setting */

src/test/regress/expected/alter_table.out

+5-1
Original file line numberDiff line numberDiff line change
@@ -2231,7 +2231,7 @@ alter table recur1 add column f2 int;
22312231
alter table recur1 alter column f2 type recur2; -- fails
22322232
ERROR: composite type recur1 cannot be made a member of itself
22332233
-- SET STORAGE may need to add a TOAST table
2234-
create table test_storage (a text);
2234+
create table test_storage (a text, c text storage plain);
22352235
alter table test_storage alter a set storage plain;
22362236
alter table test_storage add b int default 0; -- rewrite table to remove its TOAST table
22372237
alter table test_storage alter a set storage extended; -- re-add TOAST table
@@ -2243,6 +2243,9 @@ where oid = 'test_storage'::regclass;
22432243
t
22442244
(1 row)
22452245

2246+
--check STORAGE correctness
2247+
create table test_storage_failed (a text, b int storage extended);
2248+
ERROR: column data type integer can only have storage PLAIN
22462249
-- test that SET STORAGE propagates to index correctly
22472250
create index test_storage_idx on test_storage (b, a);
22482251
alter table test_storage alter column a set storage external;
@@ -2251,6 +2254,7 @@ alter table test_storage alter column a set storage external;
22512254
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
22522255
--------+---------+-----------+----------+---------+----------+--------------+-------------
22532256
a | text | | | | external | |
2257+
c | text | | | | plain | |
22542258
b | integer | | | 0 | plain | |
22552259
Indexes:
22562260
"test_storage_idx" btree (b, a)

src/test/regress/sql/alter_table.sql

+4-1
Original file line numberDiff line numberDiff line change
@@ -1516,7 +1516,7 @@ alter table recur1 add column f2 int;
15161516
alter table recur1 alter column f2 type recur2; -- fails
15171517

15181518
-- SET STORAGE may need to add a TOAST table
1519-
create table test_storage (a text);
1519+
create table test_storage (a text, c text storage plain);
15201520
alter table test_storage alter a set storage plain;
15211521
alter table test_storage add b int default 0; -- rewrite table to remove its TOAST table
15221522
alter table test_storage alter a set storage extended; -- re-add TOAST table
@@ -1525,6 +1525,9 @@ select reltoastrelid <> 0 as has_toast_table
15251525
from pg_class
15261526
where oid = 'test_storage'::regclass;
15271527

1528+
--check STORAGE correctness
1529+
create table test_storage_failed (a text, b int storage extended);
1530+
15281531
-- test that SET STORAGE propagates to index correctly
15291532
create index test_storage_idx on test_storage (b, a);
15301533
alter table test_storage alter column a set storage external;

0 commit comments

Comments
 (0)