From 7ead9925ff94cfc9c3237672b7d19d5cc1b27ef5 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 6 Jan 2022 16:46:46 -0500 Subject: [PATCH] Prevent altering partitioned table's rowtype, if it's used elsewhere. We disallow altering a column datatype within a regular table, if the table's rowtype is used as a column type elsewhere, because we lack code to go around and rewrite the other tables. This restriction should apply to partitioned tables as well, but it was not checked because ATRewriteTables and ATPrepAlterColumnType were not on the same page about who should do it for which relkinds. Per bug #17351 from Alexander Lakhin. Back-patch to all supported branches. Discussion: https://postgr.es/m/17351-6db1870f3f4f612a@postgresql.org --- src/backend/commands/tablecmds.c | 7 +++---- src/test/regress/expected/alter_table.out | 23 ++++++++++++++++++----- src/test/regress/sql/alter_table.sql | 19 +++++++++++++++---- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 89bc865e28..23364d3c7e 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -12183,12 +12183,11 @@ ATPrepAlterColumnType(List **wqueue, errmsg("\"%s\" is not a table", RelationGetRelationName(rel)))); - if (tab->relkind == RELKIND_COMPOSITE_TYPE || - tab->relkind == RELKIND_FOREIGN_TABLE) + if (!RELKIND_HAS_STORAGE(tab->relkind)) { /* - * For composite types and foreign tables, do this check now. Regular - * tables will check it later when the table is being rewritten. + * For relations without storage, do this check now. Regular tables + * will check it later when the table is being rewritten. */ find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL); } diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 24d1c7cd28..16e0475663 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2061,11 +2061,24 @@ begin; create table skip_wal_skip_rewrite_index (c varchar(10) primary key); alter table skip_wal_skip_rewrite_index alter c type varchar(20); commit; --- table's row type -create table tab1 (a int, b text); -create table tab2 (x int, y tab1); -alter table tab1 alter column b type varchar; -- fails -ERROR: cannot alter table "tab1" because column "tab2.y" uses its row type +-- We disallow changing table's row type if it's used for storage +create table at_tab1 (a int, b text); +create table at_tab2 (x int, y at_tab1); +alter table at_tab1 alter column b type varchar; -- fails +ERROR: cannot alter table "at_tab1" because column "at_tab2.y" uses its row type +drop table at_tab2; +-- Use of row type in an expression is defended differently +create table at_tab2 (x int, y text, check((x,y)::at_tab1 = (1,'42')::at_tab1)); +alter table at_tab1 alter column b type varchar; -- allowed, but ... +insert into at_tab2 values(1,'42'); -- ... this will fail +ERROR: ROW() column has type text instead of type character varying +drop table at_tab1, at_tab2; +-- Check it for a partitioned table, too +create table at_tab1 (a int, b text) partition by list(a); +create table at_tab2 (x int, y at_tab1); +alter table at_tab1 alter column b type varchar; -- fails +ERROR: cannot alter table "at_tab1" because column "at_tab2.y" uses its row type +drop table at_tab1, at_tab2; -- Alter column type that's part of a partitioned index create table at_partitioned (a int, b text) partition by range (a); create table at_part_1 partition of at_partitioned for values from (0) to (1000); diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index 5fac2585d9..ac894c0602 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -1420,10 +1420,21 @@ create table skip_wal_skip_rewrite_index (c varchar(10) primary key); alter table skip_wal_skip_rewrite_index alter c type varchar(20); commit; --- table's row type -create table tab1 (a int, b text); -create table tab2 (x int, y tab1); -alter table tab1 alter column b type varchar; -- fails +-- We disallow changing table's row type if it's used for storage +create table at_tab1 (a int, b text); +create table at_tab2 (x int, y at_tab1); +alter table at_tab1 alter column b type varchar; -- fails +drop table at_tab2; +-- Use of row type in an expression is defended differently +create table at_tab2 (x int, y text, check((x,y)::at_tab1 = (1,'42')::at_tab1)); +alter table at_tab1 alter column b type varchar; -- allowed, but ... +insert into at_tab2 values(1,'42'); -- ... this will fail +drop table at_tab1, at_tab2; +-- Check it for a partitioned table, too +create table at_tab1 (a int, b text) partition by list(a); +create table at_tab2 (x int, y at_tab1); +alter table at_tab1 alter column b type varchar; -- fails +drop table at_tab1, at_tab2; -- Alter column type that's part of a partitioned index create table at_partitioned (a int, b text) partition by range (a);