Propagate ALTER TABLE ... SET STORAGE to indexes

When creating a new index, the attstorage setting of the table column
is copied to regular (non-expression) index columns.  But a later
ALTER TABLE ... SET STORAGE is not propagated to indexes, thus
creating an inconsistent and undumpable state.

Discussion: https://www.postgresql.org/message-id/flat/9765d72b-37c0-06f5-e349-2a580aafd989%402ndquadrant.com
This commit is contained in:
Peter Eisentraut 2020-04-09 14:10:01 +02:00
parent f918b5d219
commit 03a8a5f2d4
5 changed files with 83 additions and 0 deletions

View File

@ -305,6 +305,10 @@ ALTER TABLE toasted_several REPLICA IDENTITY FULL;
ALTER TABLE toasted_several ALTER COLUMN toasted_key SET STORAGE EXTERNAL;
ALTER TABLE toasted_several ALTER COLUMN toasted_col1 SET STORAGE EXTERNAL;
ALTER TABLE toasted_several ALTER COLUMN toasted_col2 SET STORAGE EXTERNAL;
-- Change the storage of the index back to EXTENDED, separately from
-- the table. This is currently not doable via DDL, but it is
-- supported internally.
UPDATE pg_attribute SET attstorage = 'x' WHERE attrelid = 'toasted_several_pkey'::regclass AND attname = 'toasted_key';
INSERT INTO toasted_several(toasted_key) VALUES(repeat('9876543210', 10000));
SELECT pg_column_size(toasted_key) > 2^16 FROM toasted_several;
?column?

View File

@ -279,6 +279,11 @@ ALTER TABLE toasted_several ALTER COLUMN toasted_key SET STORAGE EXTERNAL;
ALTER TABLE toasted_several ALTER COLUMN toasted_col1 SET STORAGE EXTERNAL;
ALTER TABLE toasted_several ALTER COLUMN toasted_col2 SET STORAGE EXTERNAL;
-- Change the storage of the index back to EXTENDED, separately from
-- the table. This is currently not doable via DDL, but it is
-- supported internally.
UPDATE pg_attribute SET attstorage = 'x' WHERE attrelid = 'toasted_several_pkey'::regclass AND attname = 'toasted_key';
INSERT INTO toasted_several(toasted_key) VALUES(repeat('9876543210', 10000));
SELECT pg_column_size(toasted_key) > 2^16 FROM toasted_several;

View File

@ -6734,6 +6734,7 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc
Form_pg_attribute attrtuple;
AttrNumber attnum;
ObjectAddress address;
ListCell *lc;
Assert(IsA(newValue, String));
storagemode = strVal(newValue);
@ -6793,6 +6794,53 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE loc
heap_freetuple(tuple);
/*
* Apply the change to indexes as well (only for simple index columns,
* matching behavior of index.c ConstructTupleDescriptor()).
*/
foreach(lc, RelationGetIndexList(rel))
{
Oid indexoid = lfirst_oid(lc);
Relation indrel;
int i;
AttrNumber indattnum = 0;
indrel = index_open(indexoid, lockmode);
for (i = 0; i < indrel->rd_index->indnatts; i++)
{
if (indrel->rd_index->indkey.values[i] == attnum)
{
indattnum = i + 1;
break;
}
}
if (indattnum == 0)
{
index_close(indrel, lockmode);
continue;
}
tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
if (HeapTupleIsValid(tuple))
{
attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
attrtuple->attstorage = newstorage;
CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
InvokeObjectPostAlterHook(RelationRelationId,
RelationGetRelid(rel),
attrtuple->attnum);
heap_freetuple(tuple);
}
index_close(indrel, lockmode);
}
heap_close(attrelation, RowExclusiveLock);
ObjectAddressSubSet(address, RelationRelationId,

View File

@ -2255,6 +2255,26 @@ where oid = 'test_storage'::regclass;
t
(1 row)
-- test that SET STORAGE propagates to index correctly
create index test_storage_idx on test_storage (b, a);
alter table test_storage alter column a set storage external;
\d+ test_storage
Table "public.test_storage"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+---------+----------+--------------+-------------
a | text | | | | external | |
b | integer | | | 0 | plain | |
Indexes:
"test_storage_idx" btree (b, a)
\d+ test_storage_idx
Index "public.test_storage_idx"
Column | Type | Key? | Definition | Storage | Stats target
--------+---------+------+------------+----------+--------------
b | integer | yes | b | plain |
a | text | yes | a | external |
btree, for table "public.test_storage"
-- ALTER COLUMN TYPE with a check constraint and a child table (bug #13779)
CREATE TABLE test_inh_check (a float check (a > 10.2), b float);
CREATE TABLE test_inh_check_child() INHERITS(test_inh_check);

View File

@ -1503,6 +1503,12 @@ select reltoastrelid <> 0 as has_toast_table
from pg_class
where oid = 'test_storage'::regclass;
-- test that SET STORAGE propagates to index correctly
create index test_storage_idx on test_storage (b, a);
alter table test_storage alter column a set storage external;
\d+ test_storage
\d+ test_storage_idx
-- ALTER COLUMN TYPE with a check constraint and a child table (bug #13779)
CREATE TABLE test_inh_check (a float check (a > 10.2), b float);
CREATE TABLE test_inh_check_child() INHERITS(test_inh_check);