pgsql-fix-inherit-rename.3.patch
application/octect-stream
Filename: pgsql-fix-inherit-rename.3.patch
Type: application/octect-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: context
| File | + | − |
|---|---|---|
| src/backend/catalog/pg_inherits.c | 61 | 0 |
| src/backend/commands/alter.c | 1 | 0 |
| src/backend/commands/tablecmds.c | 49 | 11 |
| src/include/catalog/pg_inherits_fn.h | 4 | 0 |
| src/include/commands/tablecmds.h | 1 | 0 |
| src/test/regress/expected/inherit.out | 17 | 0 |
| src/test/regress/sql/inherit.sql | 12 | 0 |
Index: base/src/test/regress/sql/inherit.sql
===================================================================
*** base/src/test/regress/sql/inherit.sql (revision 2538)
--- base/src/test/regress/sql/inherit.sql (working copy)
*************** CREATE TABLE inh_error1 () INHERITS (t1,
*** 334,336 ****
--- 334,348 ----
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
+ CREATE TABLE t1 (a int, b int);
+ CREATE TABLE t2 (b int, c int);
+ CREATE TABLE t3 (d int) INHERITS(t1, t2);
+ ALTER TABLE t1 RENAME a TO x;
+ ALTER TABLE t1 RENAME b TO y; -- to be failed
+ ALTER TABLE t3 RENAME d TO z;
+ SELECT * FROM t3;
+ DROP TABLE t3;
+ DROP TABLE t2;
+ DROP TABLE t1;
Index: base/src/test/regress/expected/inherit.out
===================================================================
*** base/src/test/regress/expected/inherit.out (revision 2538)
--- base/src/test/regress/expected/inherit.out (working copy)
*************** NOTICE: merging column "a" with inherit
*** 1053,1055 ****
--- 1053,1072 ----
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
+ CREATE TABLE t1 (a int, b int);
+ CREATE TABLE t2 (b int, c int);
+ CREATE TABLE t3 (d int) INHERITS(t1, t2);
+ NOTICE: merging multiple inherited definitions of column "b"
+ ALTER TABLE t1 RENAME a TO x;
+ ALTER TABLE t1 RENAME b TO y; -- to be failed
+ ERROR: cannot rename multiple inherited column "b"
+ ALTER TABLE t3 RENAME d TO z;
+ SELECT * FROM t3;
+ x | b | c | z
+ ---+---+---+---
+ (0 rows)
+
+ DROP TABLE t3;
+ DROP TABLE t2;
+ DROP TABLE t1;
Index: base/src/include/commands/tablecmds.h
===================================================================
*** base/src/include/commands/tablecmds.h (revision 2538)
--- base/src/include/commands/tablecmds.h (working copy)
*************** extern void ExecuteTruncate(TruncateStmt
*** 42,47 ****
--- 42,48 ----
extern void renameatt(Oid myrelid,
const char *oldattname,
const char *newattname,
+ int expected_inhcount,
bool recurse,
bool recursing);
Index: base/src/include/catalog/pg_inherits_fn.h
===================================================================
*** base/src/include/catalog/pg_inherits_fn.h (revision 2538)
--- base/src/include/catalog/pg_inherits_fn.h (working copy)
***************
*** 19,24 ****
--- 19,28 ----
extern List *find_inheritance_children(Oid parentrelId, LOCKMODE lockmode);
extern List *find_all_inheritors(Oid parentrelId, LOCKMODE lockmode);
+ extern void find_all_inheritors_with_inhcount(Oid parentrelId,
+ LOCKMODE lockmode,
+ List **child_oids,
+ List **child_inhs);
extern bool has_subclass(Oid relationId);
extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);
Index: base/src/backend/commands/tablecmds.c
===================================================================
*** base/src/backend/commands/tablecmds.c (revision 2538)
--- base/src/backend/commands/tablecmds.c (working copy)
*************** void
*** 1908,1913 ****
--- 1908,1914 ----
renameatt(Oid myrelid,
const char *oldattname,
const char *newattname,
+ int expected_inhcount,
bool recurse,
bool recursing)
{
*************** renameatt(Oid myrelid,
*** 1948,1971 ****
*/
if (recurse)
{
! ListCell *child;
! List *children;
!
! children = find_all_inheritors(myrelid, AccessExclusiveLock);
/*
! * find_all_inheritors does the recursive search of the inheritance
! * hierarchy, so all we have to do is process all of the relids in the
! * list that it returns.
*/
! foreach(child, children)
{
! Oid childrelid = lfirst_oid(child);
! if (childrelid == myrelid)
continue;
/* note we need not recurse again */
! renameatt(childrelid, oldattname, newattname, false, true);
}
}
else
--- 1949,1998 ----
*/
if (recurse)
{
! List *child_oids, *child_inhs;
! ListCell *lo, *li;
/*
! * If we cound find an expected inhcount larger than 1,
! * t means the relation has diamond inheritance tree.
! *
! * If an attribute to be renamed is inherited from multiple
! * parent relations independently defined then merged, it
! * is problematic to rename the attribute, because anohter
! * inheritance tree assumes the child relation still has the
! * inherited attributes, but it will be incorrect.
! * So, we have to forbide to rename an attribute which is
! * inherited from multiple parent relations.
! *
! * However, we have an exception case when the attribute is
! * inherited from multiple parent relations, but these ones
! * eventually have same origion.
! * For example, when TBL_A is inherited by TBL_X and TBL_Y,
! * these tables are inherited by TBL_XY, the TBL_XY indeed
! * has multiple parent relations and its attributes was
! * merged. But, these have same origin, and we have no reason
! * to prevent renaming them.
! *
! * So, we check whether the child relation has diamond
! * inheritance, or not, here. If pg_attribute.attinhcount is
! * larger than expected inhcount, it means the attribute was
! * merged with any other attributes come from other parent
! * relations.
*/
! find_all_inheritors_with_inhcount(myrelid, AccessExclusiveLock,
! &child_oids, &child_inhs);
!
! forboth (lo, child_oids, li, child_inhs)
{
! Oid child_relid = lfirst_oid(lo);
! int child_inhcount = lfirst_int(li);
! if (child_relid == myrelid)
continue;
+
/* note we need not recurse again */
! renameatt(child_relid, oldattname, newattname,
! child_inhcount, false, true);
}
}
else
*************** renameatt(Oid myrelid,
*** 2009,2014 ****
--- 2036,2052 ----
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 (attform->attinhcount > expected_inhcount)
+ 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 */
Index: base/src/backend/commands/alter.c
===================================================================
*** base/src/backend/commands/alter.c (revision 2538)
--- base/src/backend/commands/alter.c (working copy)
*************** ExecRenameStmt(RenameStmt *stmt)
*** 125,130 ****
--- 125,131 ----
renameatt(relid,
stmt->subname, /* old att name */
stmt->newname, /* new att name */
+ 1, /* expected inhcount */
interpretInhOption(stmt->relation->inhOpt), /* recursive? */
false); /* recursing already? */
break;
Index: base/src/backend/catalog/pg_inherits.c
===================================================================
*** base/src/backend/catalog/pg_inherits.c (revision 2538)
--- base/src/backend/catalog/pg_inherits.c (working copy)
*************** find_all_inheritors(Oid parentrelId, LOC
*** 190,195 ****
--- 190,256 ----
return rels_list;
}
+ /*
+ * find_all_inheritors_with_inhcount
+ *
+ * It is same as find_all_inheritors, except for it returns an excepted
+ * inhcount for each children.
+ */
+ void
+ find_all_inheritors_with_inhcount(Oid parentrelId, LOCKMODE lockmode,
+ List **child_oids, List **child_inhs)
+ {
+ List *children, *rels_oids, *rels_inhs;
+ ListCell *lc, *lo, *li;
+
+ /*
+ * The root of the inheritance tree always has 1 as an expected
+ * inhcount.
+ */
+ rels_oids = list_make1_oid(parentrelId);
+ rels_inhs = list_make1_int(1);
+
+ children = find_inheritance_children(parentrelId, lockmode);
+ foreach (lc, children)
+ {
+ Oid cur_relid = lfirst_oid(lc);
+ List *cur_children;
+ bool found = false;
+
+ forboth (lo, rels_oids, li, rels_inhs)
+ {
+ /*
+ * If we found a duplicated relation within a certain inheritance
+ * tree, it means a diamond-inheritance. So, we increment its
+ * expected inhcount without appending an element to the list.
+ */
+ if (lfirst_oid(lo) == cur_relid)
+ {
+ lfirst_int(li)++;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ rels_oids = lappend_oid(rels_oids, cur_relid);
+ rels_inhs = lappend_int(rels_inhs, 1);
+ }
+
+ /*
+ * Unlike find_all_inheritors(), we need to walk on child relations
+ * that have diamond inheritance tree, because this function has to
+ * return correct expected inhecount to the caller.
+ */
+ cur_children = find_inheritance_children(cur_relid, lockmode);
+
+ children = list_concat(children, cur_children);
+ }
+
+ *child_oids = rels_oids;
+ *child_inhs = rels_inhs;
+ }
/*
* has_subclass - does this relation have any children?