Skip to content

Commit 4514322

Browse files
committed
Disallow changing an inherited column's type if not all parents changed.
If a table inherits from multiple unrelated parents, we must disallow changing the type of a column inherited from multiple such parents, else it would be out of step with the other parents. However, it's possible for the column to ultimately be inherited from just one common ancestor, in which case a change starting from that ancestor should still be allowed. (I would not be excited about preserving that option, were it not that we have regression test cases exercising it already ...) It's slightly annoying that this patch looks different from the logic with the same end goal in renameatt(), and more annoying that it requires an extra syscache lookup to make the test. However, the recursion logic is quite different in the two functions, and a back-patched bug fix is no place to be trying to unify them. Per report from Manuel Rigger. Back-patch to 9.5. The bug exists in 9.4 too (and doubtless much further back); but the way the recursion is done in 9.4 is a good bit different, so that substantial refactoring would be needed to fix it in 9.4. I'm disinclined to do that, or risk introducing new bugs, for a bug that has escaped notice for this long. Discussion: https://postgr.es/m/CA+u7OA4qogDv9rz1HAb-ADxttXYPqQdUdPY_yd4kCzywNxRQXA@mail.gmail.com
1 parent 6088696 commit 4514322

File tree

3 files changed

+58
-6
lines changed

3 files changed

+58
-6
lines changed

src/backend/commands/tablecmds.c

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8768,7 +8768,11 @@ ATPrepAlterColumnType(List **wqueue,
87688768
errmsg("cannot alter system column \"%s\"",
87698769
colName)));
87708770

8771-
/* Don't alter inherited columns */
8771+
/*
8772+
* Don't alter inherited columns. At outer level, there had better not be
8773+
* any inherited definition; when recursing, we assume this was checked at
8774+
* the parent level (see below).
8775+
*/
87728776
if (attTup->attinhcount > 0 && !recursing)
87738777
ereport(ERROR,
87748778
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
@@ -8892,20 +8896,26 @@ ATPrepAlterColumnType(List **wqueue,
88928896
if (recurse)
88938897
{
88948898
Oid relid = RelationGetRelid(rel);
8895-
ListCell *child;
8896-
List *children;
8899+
List *child_oids,
8900+
*child_numparents;
8901+
ListCell *lo,
8902+
*li;
88978903

8898-
children = find_all_inheritors(relid, lockmode, NULL);
8904+
child_oids = find_all_inheritors(relid, lockmode,
8905+
&child_numparents);
88998906

89008907
/*
89018908
* find_all_inheritors does the recursive search of the inheritance
89028909
* hierarchy, so all we have to do is process all of the relids in the
89038910
* list that it returns.
89048911
*/
8905-
foreach(child, children)
8912+
forboth(lo, child_oids, li, child_numparents)
89068913
{
8907-
Oid childrelid = lfirst_oid(child);
8914+
Oid childrelid = lfirst_oid(lo);
8915+
int numparents = lfirst_int(li);
89088916
Relation childrel;
8917+
HeapTuple childtuple;
8918+
Form_pg_attribute childattTup;
89098919

89108920
if (childrelid == relid)
89118921
continue;
@@ -8914,6 +8924,29 @@ ATPrepAlterColumnType(List **wqueue,
89148924
childrel = relation_open(childrelid, NoLock);
89158925
CheckTableNotInUse(childrel, "ALTER TABLE");
89168926

8927+
/*
8928+
* Verify that the child doesn't have any inherited definitions of
8929+
* this column that came from outside this inheritance hierarchy.
8930+
* (renameatt makes a similar test, though in a different way
8931+
* because of its different recursion mechanism.)
8932+
*/
8933+
childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
8934+
colName);
8935+
if (!HeapTupleIsValid(childtuple))
8936+
ereport(ERROR,
8937+
(errcode(ERRCODE_UNDEFINED_COLUMN),
8938+
errmsg("column \"%s\" of relation \"%s\" does not exist",
8939+
colName, RelationGetRelationName(childrel))));
8940+
childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
8941+
8942+
if (childattTup->attinhcount > numparents)
8943+
ereport(ERROR,
8944+
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8945+
errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
8946+
colName, RelationGetRelationName(childrel))));
8947+
8948+
ReleaseSysCache(childtuple);
8949+
89178950
/*
89188951
* Remap the attribute numbers. If no USING expression was
89198952
* specified, there is no need for this step.

src/test/regress/expected/inherit.out

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,16 @@ select * from d;
701701
32 | one | two | three
702702
(1 row)
703703

704+
-- The above verified that we can change the type of a multiply-inherited
705+
-- column; but we should reject that if any definition was inherited from
706+
-- an unrelated parent.
707+
create temp table parent1(f1 int, f2 int);
708+
create temp table parent2(f1 int, f3 bigint);
709+
create temp table childtab(f4 int) inherits(parent1, parent2);
710+
NOTICE: merging multiple inherited definitions of column "f1"
711+
alter table parent1 alter column f1 type bigint; -- fail, conflict w/parent2
712+
ERROR: cannot alter inherited column "f1" of relation "childtab"
713+
alter table parent1 alter column f2 type bigint; -- ok
704714
-- check that oid column is handled properly during alter table inherit
705715
create table oid_parent (a int) with oids;
706716
create table oid_child () inherits (oid_parent);

src/test/regress/sql/inherit.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,15 @@ insert into d values('test','one','two','three');
191191
alter table a alter column aa type integer using bit_length(aa);
192192
select * from d;
193193

194+
-- The above verified that we can change the type of a multiply-inherited
195+
-- column; but we should reject that if any definition was inherited from
196+
-- an unrelated parent.
197+
create temp table parent1(f1 int, f2 int);
198+
create temp table parent2(f1 int, f3 bigint);
199+
create temp table childtab(f4 int) inherits(parent1, parent2);
200+
alter table parent1 alter column f1 type bigint; -- fail, conflict w/parent2
201+
alter table parent1 alter column f2 type bigint; -- ok
202+
194203
-- check that oid column is handled properly during alter table inherit
195204
create table oid_parent (a int) with oids;
196205

0 commit comments

Comments
 (0)