*** a/src/backend/catalog/pg_inherits.c --- b/src/backend/catalog/pg_inherits.c *************** *** 28,33 **** --- 28,34 ---- #include "parser/parse_type.h" #include "storage/lmgr.h" #include "utils/fmgroids.h" + #include "utils/lsyscache.h" #include "utils/syscache.h" #include "utils/tqual.h" *************** *** 190,195 **** find_all_inheritors(Oid parentrelId, LOCKMODE lockmode) --- 191,257 ---- return rels_list; } + /* + * find_column_origin + * + * It returns a list of relation OIDs that defines the given column originally. + * In most cases, a certain column has one origin, unless it (or its parent) + * has multiple inheritance tree. + * Note that it returns a unique OID for diamond-type inheritance tree, because + * it shares same origin. + */ + List * + find_column_origin(Oid relOid, const char *colName) + { + Relation inhRel; + SysScanDesc scan; + ScanKeyData skey[1]; + HeapTuple tuple; + List *results = NIL; + + /* + * This relation does not contain the given column, is also means + * the parent relation also. + */ + if (get_attnum(relOid, colName) == InvalidAttrNumber) + return NIL; + + inhRel = heap_open(InheritsRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], + Anum_pg_inherits_inhrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relOid)); + + scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId, + true, SnapshotNow, 1, &skey); + + while (HeapTupleIsValid(tuple = systable_getnext(scan))) + { + Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(tuple); + List *temp; + + temp = find_column_origin(inhForm->inhparent, colName); + if (temp == NIL) + continue; + + /* + * Add to the queue only those children not already seen. This avoids + * making duplicate entries in case of multiple inheritance paths from + * the same parent. (It'll also keep us from getting into an infinite + * loop, though theoretically there can't be any cycles in the + * inheritance graph anyway.) + */ + results = list_concat_unique_oid(results, temp); + } + systable_endscan(scan); + heap_close(inhRel, AccessShareLock); + + if (results == NIL) + results = list_make1_oid(relOid); + + return results; + } /* * has_subclass - does this relation have any children? *** a/src/backend/commands/tablecmds.c --- b/src/backend/commands/tablecmds.c *************** *** 2009,2014 **** renameatt(Oid myrelid, --- 2009,2025 ---- errmsg("cannot rename inherited column \"%s\"", oldattname))); + /* + * if the attribute is multiple inherited, forbid the renaming, + * even if we are already inside a recursive rename, because we + * have no reasonable way to keep its integrity. + */ + if (list_length(find_column_origin(myrelid, oldattname)) > 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + (errmsg("cannot rename multiple inherited column \"%s\"", + oldattname)))); + /* new name should not already exist */ /* this test is deliberately not attisdropped-aware */ *************** *** 5771,5776 **** ATPrepAlterColumnType(List **wqueue, --- 5782,5788 ---- bool recurse, bool recursing, AlterTableCmd *cmd) { + Oid relOid = RelationGetRelid(rel); char *colName = cmd->name; TypeName *typeName = (TypeName *) cmd->def; HeapTuple tuple; *************** *** 5806,5811 **** ATPrepAlterColumnType(List **wqueue, --- 5818,5830 ---- errmsg("cannot alter inherited column \"%s\"", colName))); + /* Don't alter multiple inherited columns */ + if (list_length(find_column_origin(relOid, colName)) > 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + (errmsg("cannot alter multiple inherited column \"%s\"", + colName)))); + /* Look up the target type */ targettype = typenameTypeId(NULL, typeName, &targettypmod); *** a/src/include/catalog/pg_inherits_fn.h --- b/src/include/catalog/pg_inherits_fn.h *************** *** 19,24 **** --- 19,25 ---- extern List *find_inheritance_children(Oid parentrelId, LOCKMODE lockmode); extern List *find_all_inheritors(Oid parentrelId, LOCKMODE lockmode); + extern List *find_column_origin(Oid relOid, const char *colName); extern bool has_subclass(Oid relationId); extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId); *** a/src/test/regress/expected/inherit.out --- b/src/test/regress/expected/inherit.out *************** *** 1053,1055 **** NOTICE: merging column "a" with inherited definition --- 1053,1133 ---- ERROR: column "a" has a storage parameter conflict DETAIL: MAIN versus EXTENDED DROP TABLE t1, t2, t3, t4, t12_storage, t12_comments, t1_inh, t13_inh, t13_like, t_all; + -- Test for renaming and type changes (simple multiple inheritance) + CREATE TABLE t1 (a int, b int); + CREATE TABLE s1 (b int, c int); + CREATE TABLE ts (d int) INHERITS (t1, s1); + NOTICE: merging multiple inherited definitions of column "b" + ALTER TABLE s1 ALTER b TYPE float; -- to be failed + ERROR: cannot alter multiple inherited column "b" + ALTER TABLE s1 ALTER c TYPE float; + ALTER TABLE ts ALTER c TYPE text; -- to be failed + ERROR: cannot alter inherited column "c" + ALTER TABLE ts ALTER d TYPE text; + ALTER TABLE t1 RENAME a TO aa; + ALTER TABLE t1 RENAME b TO bb; -- to be failed + ERROR: cannot rename multiple inherited column "b" + ALTER TABLE ts RENAME aa TO aaa; -- to be failed + ERROR: cannot rename inherited column "aa" + ALTER TABLE ts RENAME d TO dd; + \d+ ts + Table "public.ts" + Column | Type | Modifiers | Storage | Description + --------+------------------+-----------+----------+------------- + aa | integer | | plain | + b | integer | | plain | + c | double precision | | plain | + dd | text | | extended | + Inherits: t1, + s1 + Has OIDs: no + + DROP TABLE ts; + -- Test for renaming and type changes (diamond inheritance) + CREATE TABLE t2 (x int) INHERITS (t1); + CREATE TABLE t3 (y int) INHERITS (t1); + CREATE TABLE t4 (z int) INHERITS (t2, t3); + NOTICE: merging multiple inherited definitions of column "aa" + NOTICE: merging multiple inherited definitions of column "b" + ALTER TABLE t1 ALTER aa TYPE float; + ALTER TABLE t1 RENAME aa TO aaa; + \d+ t4 + Table "public.t4" + Column | Type | Modifiers | Storage | Description + --------+------------------+-----------+---------+------------- + aaa | double precision | | plain | + b | integer | | plain | + x | integer | | plain | + y | integer | | plain | + z | integer | | plain | + Inherits: t2, + t3 + Has OIDs: no + + CREATE TABLE ts (d int) INHERITS (t2, s1); + NOTICE: merging multiple inherited definitions of column "b" + ALTER TABLE t1 ALTER aaa TYPE text; + ALTER TABLE t1 ALTER b TYPE text; -- to be failed + ERROR: cannot alter multiple inherited column "b" + ALTER TABLE t1 RENAME aaa TO aaaa; + ALTER TABLE t1 RENAME b TO bb; -- to be failed + ERROR: cannot rename multiple inherited column "b" + \d+ ts + Table "public.ts" + Column | Type | Modifiers | Storage | Description + --------+------------------+-----------+----------+------------- + aaaa | text | | extended | + b | integer | | plain | + x | integer | | plain | + c | double precision | | plain | + d | integer | | plain | + Inherits: t2, + s1 + Has OIDs: no + + DROP TABLE t1, s1 CASCADE; + NOTICE: drop cascades to 4 other objects + DETAIL: drop cascades to table t2 + drop cascades to table ts + drop cascades to table t3 + drop cascades to table t4 *** a/src/test/regress/sql/inherit.sql --- b/src/test/regress/sql/inherit.sql *************** *** 334,336 **** CREATE TABLE inh_error1 () INHERITS (t1, t4); --- 334,372 ---- CREATE TABLE inh_error2 (LIKE t4 INCLUDING STORAGE) INHERITS (t1); DROP TABLE t1, t2, t3, t4, t12_storage, t12_comments, t1_inh, t13_inh, t13_like, t_all; + + -- Test for renaming and type changes (simple multiple inheritance) + CREATE TABLE t1 (a int, b int); + CREATE TABLE s1 (b int, c int); + CREATE TABLE ts (d int) INHERITS (t1, s1); + + ALTER TABLE s1 ALTER b TYPE float; -- to be failed + ALTER TABLE s1 ALTER c TYPE float; + ALTER TABLE ts ALTER c TYPE text; -- to be failed + ALTER TABLE ts ALTER d TYPE text; + + ALTER TABLE t1 RENAME a TO aa; + ALTER TABLE t1 RENAME b TO bb; -- to be failed + ALTER TABLE ts RENAME aa TO aaa; -- to be failed + ALTER TABLE ts RENAME d TO dd; + \d+ ts + + DROP TABLE ts; + + -- Test for renaming and type changes (diamond inheritance) + CREATE TABLE t2 (x int) INHERITS (t1); + CREATE TABLE t3 (y int) INHERITS (t1); + CREATE TABLE t4 (z int) INHERITS (t2, t3); + + ALTER TABLE t1 ALTER aa TYPE float; + ALTER TABLE t1 RENAME aa TO aaa; + \d+ t4 + + CREATE TABLE ts (d int) INHERITS (t2, s1); + ALTER TABLE t1 ALTER aaa TYPE text; + ALTER TABLE t1 ALTER b TYPE text; -- to be failed + ALTER TABLE t1 RENAME aaa TO aaaa; + ALTER TABLE t1 RENAME b TO bb; -- to be failed + \d+ ts + + DROP TABLE t1, s1 CASCADE;