diff --git a/contrib/test_decoding/expected/toast.out b/contrib/test_decoding/expected/toast.out index 91a9a1e86d6..75c4d22d801 100644 --- a/contrib/test_decoding/expected/toast.out +++ b/contrib/test_decoding/expected/toast.out @@ -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? diff --git a/contrib/test_decoding/sql/toast.sql b/contrib/test_decoding/sql/toast.sql index ef11d2bfff9..016c3ab7842 100644 --- a/contrib/test_decoding/sql/toast.sql +++ b/contrib/test_decoding/sql/toast.sql @@ -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; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 49d7059d535..bc66d6c4f5f 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -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, diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index a65f706deee..17b37708128 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -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); diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index 9cdb92670e0..b211a2b90b4 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -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);