]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Propagate ALTER TABLE ... SET STORAGE to indexes
authorPeter Eisentraut <peter@eisentraut.org>
Thu, 9 Apr 2020 12:10:01 +0000 (14:10 +0200)
committerPeter Eisentraut <peter@eisentraut.org>
Fri, 8 May 2020 07:30:57 +0000 (09:30 +0200)
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

contrib/test_decoding/expected/toast.out
contrib/test_decoding/sql/toast.sql
src/backend/commands/tablecmds.c
src/test/regress/expected/alter_table.out
src/test/regress/sql/alter_table.sql

index 91a9a1e86d6c630d3b4dd12efe4f75eef090a492..75c4d22d8013136ed0019746a29b15d236ced6e8 100644 (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? 
index ef11d2bfff999a3ed195c2dca7ae2192634cc752..016c3ab78424986ff80e4089cc9a5a277c49ad8f 100644 (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;
 
index 49d7059d535e216eca6e5eda6bd672dad516364c..bc66d6c4f5f1de4cad4b04701e6d579c0d671f58 100644 (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,
index a65f706deeec9f75dbaba059c8f9a52d6cc419cd..17b37708128e35c6e940a5f039694810d578ff56 100644 (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);
index 9cdb92670e010f445bc223eca170fa9647bc6089..b211a2b90b4a845b3753cf9e15f4a2290e1f903c 100644 (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);