pg-fix-inherit-rename-type-review.patch
application/octet-stream
Filename: pg-fix-inherit-rename-type-review.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/catalog/pg_inherits.c | 62 | 0 |
| src/backend/commands/tablecmds.c | 19 | 0 |
| src/include/catalog/pg_inherits_fn.h | 1 | 0 |
| src/test/regress/expected/inherit.out | 78 | 0 |
| src/test/regress/sql/inherit.sql | 36 | 0 |
diff --git a/src/backend/catalog/pg_inherits.c b/src/backend/catalog/pg_inherits.c
index e256f6f..fbde33a 100644
--- a/src/backend/catalog/pg_inherits.c
+++ b/src/backend/catalog/pg_inherits.c
@@ -28,6 +28,7 @@
#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,6 +191,67 @@ find_all_inheritors(Oid parentrelId, LOCKMODE lockmode)
return rels_list;
}
+/*
+ * find_column_origin
+ *
+ * Returns a list of relation OIDs the given column is inherited from.
+ * In most cases, a column has one origin, unless it (or its parent)
+ * has multiple ancestors.
+ * Note that find_column_origin() returns a unique OID for diamond-type
+ * inheritance trees, because all relations shares the 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, so its
+ * parent neither.
+ */
+ 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?
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 10f0a94..4cd988c 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -2009,6 +2009,17 @@ renameatt(Oid myrelid,
errmsg("cannot rename inherited column \"%s\"",
oldattname)));
+ /*
+ * if the attribute is inherited multiple times, 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 */
@@ -5768,6 +5779,7 @@ ATPrepAlterColumnType(List **wqueue,
bool recurse, bool recursing,
AlterTableCmd *cmd)
{
+ Oid relOid = RelationGetRelid(rel);
char *colName = cmd->name;
TypeName *typeName = (TypeName *) cmd->def;
HeapTuple tuple;
@@ -5803,6 +5815,13 @@ ATPrepAlterColumnType(List **wqueue,
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);
diff --git a/src/include/catalog/pg_inherits_fn.h b/src/include/catalog/pg_inherits_fn.h
index 91baec3..a23f62b 100644
--- a/src/include/catalog/pg_inherits_fn.h
+++ b/src/include/catalog/pg_inherits_fn.h
@@ -19,6 +19,7 @@
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);
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index 9c83a32..3a049cc 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -1053,3 +1053,81 @@ NOTICE: merging column "a" with inherited definition
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
diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql
index 192e860..82ee7d3 100644
--- a/src/test/regress/sql/inherit.sql
+++ b/src/test/regress/sql/inherit.sql
@@ -334,3 +334,39 @@ CREATE TABLE inh_error1 () INHERITS (t1, t4);
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;