2010/1/31 KaiGai Kohei <[email protected]>:
> The attached patch modified find_all_inheritors() to return the list of
> expected inhcount, if List * pointer is given. And, it focuses on only
> the bugs in renameatt() case.
I have cleaned up and simplified this patch. Attached is the version
I intend to commit. Changes:
- make find_all_inheritors return the list of OIDs, as previously,
instead of using an out parameter
- undo some useless variable renamings
- undo some useless whitespace changes
- rework comments
- fix regression tests to avoid using the same alias twice in the same query
- add an ORDER BY clause to the regression tests so that they pass on my machine
- improve the names of some of the new variables
- instead of adding an additional argument to renameatt(), just
replace the existing 'bool recursing' with 'int expected_parents'.
This allows merging the two versions of the "cannot rename inherited
column" message together, which seems like a reasonably good idea to
me, though if someone has a better idea that's fine. I didn't think
the one additional word added to the message provided enough clarity
to make it worth creating another translatable string.
> Also, the ALTER COLUMN TYPE case should be also fixed in the 9.1 release
> (or 9.0.1?).
If the fix is something we could commit for 9.0.1, then we ought to do
it now before 9.0 is released. If you want to submit a follow-on
patch to address ALTER COLUMN TYPE once this is committed, then please
do so.
...Robert
diff --git a/src/backend/catalog/pg_inherits.c b/src/backend/catalog/pg_inherits.c
index e256f6f..b53f44d 100644
--- a/src/backend/catalog/pg_inherits.c
+++ b/src/backend/catalog/pg_inherits.c
@@ -148,6 +148,9 @@ find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
* find_all_inheritors -
* Returns a list of relation OIDs including the given rel plus
* all relations that inherit from it, directly or indirectly.
+ * Optionally, it also returns the number of parents found for
+ * each such relation within the inheritance tree rooted at the
+ * given rel.
*
* The specified lock type is acquired on all child relations (but not on the
* given rel; caller should already have locked it). If lockmode is NoLock
@@ -155,9 +158,9 @@ find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
* against possible DROPs of child relations.
*/
List *
-find_all_inheritors(Oid parentrelId, LOCKMODE lockmode)
+find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **parents)
{
- List *rels_list;
+ List *rels_list, *rel_parents;
ListCell *l;
/*
@@ -168,11 +171,13 @@ find_all_inheritors(Oid parentrelId, LOCKMODE lockmode)
* doesn't fetch the next list element until the bottom of the loop.
*/
rels_list = list_make1_oid(parentrelId);
+ rel_parents = list_make1_int(0);
foreach(l, rels_list)
{
Oid currentrel = lfirst_oid(l);
List *currentchildren;
+ ListCell *lc;
/* Get the direct children of this rel */
currentchildren = find_inheritance_children(currentrel, lockmode);
@@ -184,9 +189,35 @@ find_all_inheritors(Oid parentrelId, LOCKMODE lockmode)
* loop, though theoretically there can't be any cycles in the
* inheritance graph anyway.)
*/
- rels_list = list_concat_unique_oid(rels_list, currentchildren);
+ foreach(lc, currentchildren)
+ {
+ Oid child_oid = lfirst_oid(lc);
+ bool found = false;
+ ListCell *lo;
+ ListCell *li;
+
+ forboth(lo, rels_list, li, rel_parents)
+ {
+ if (lfirst_oid(lo) == child_oid)
+ {
+ lfirst_int(li)++;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ rels_list = lappend_oid(rels_list, child_oid);
+ rel_parents = lappend_int(rel_parents, 1);
+ }
+ }
}
+ if (parents)
+ *parents = rel_parents;
+ else
+ list_free(rel_parents);
return rels_list;
}
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 8c81eaa..efbd088 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -126,7 +126,7 @@ ExecRenameStmt(RenameStmt *stmt)
stmt->subname, /* old att name */
stmt->newname, /* new att name */
interpretInhOption(stmt->relation->inhOpt), /* recursive? */
- false); /* recursing already? */
+ 0); /* expected inhcount */
break;
case OBJECT_TRIGGER:
renametrig(relid,
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 9062c15..c93b57f 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1390,7 +1390,8 @@ acquire_inherited_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
* Find all members of inheritance set. We only need AccessShareLock on
* the children.
*/
- tableOIDs = find_all_inheritors(RelationGetRelid(onerel), AccessShareLock);
+ tableOIDs =
+ find_all_inheritors(RelationGetRelid(onerel), AccessShareLock, NULL);
/*
* Check that there's at least one descendant, else fail. This could
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 50e4244..52f32fc 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -824,7 +824,7 @@ ExecuteTruncate(TruncateStmt *stmt)
ListCell *child;
List *children;
- children = find_all_inheritors(myrelid, AccessExclusiveLock);
+ children = find_all_inheritors(myrelid, AccessExclusiveLock, NULL);
foreach(child, children)
{
@@ -1943,7 +1943,7 @@ renameatt(Oid myrelid,
const char *oldattname,
const char *newattname,
bool recurse,
- bool recursing)
+ int expected_parents)
{
Relation targetrelation;
Relation attrelation;
@@ -1987,24 +1987,31 @@ renameatt(Oid myrelid,
*/
if (recurse)
{
- ListCell *child;
- List *children;
+ List *child_oids, *child_parents;
+ ListCell *lo, *li;
- children = find_all_inheritors(myrelid, AccessExclusiveLock);
+ /*
+ * we need the number of parents for each child so that the recursive
+ * calls to renameatt() can determine whether there are any parents
+ * outside the inheritance hierarchy being processed.
+ */
+ child_oids =
+ find_all_inheritors(myrelid, AccessExclusiveLock, &child_parents);
/*
* 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)
+ forboth(lo, child_oids, li, child_parents)
{
- Oid childrelid = lfirst_oid(child);
+ Oid childrelid = lfirst_oid(lo);
+ int numparents = lfirst_int(li);
if (childrelid == myrelid)
continue;
/* note we need not recurse again */
- renameatt(childrelid, oldattname, newattname, false, true);
+ renameatt(childrelid, oldattname, newattname, false, numparents);
}
}
else
@@ -2012,8 +2019,10 @@ renameatt(Oid myrelid,
/*
* If we are told not to recurse, there had better not be any child
* tables; else the rename would put them out of step.
+ *
+ * expected_parents will only be 0 if we are not already recursing.
*/
- if (!recursing &&
+ if (expected_parents == 0 &&
find_inheritance_children(myrelid, NoLock) != NIL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
@@ -2039,10 +2048,15 @@ renameatt(Oid myrelid,
oldattname)));
/*
- * if the attribute is inherited, forbid the renaming, unless we are
- * already inside a recursive rename.
+ * if the attribute is inherited, forbid the renaming. if this is a
+ * top-level call to renameatt(), then expected_parents will be 0, so the
+ * effect of this code will be to prohibit the renaming if the attribute
+ * is inherited at all. if this is a recursive call to renameatt(),
+ * expected_parents will be the number of parents the current relation has
+ * within the inheritance hierarchy being processed, so we'll prohibit
+ * the renaming only if there are additional parents from elsewhere.
*/
- if (attform->attinhcount > 0 && !recursing)
+ if (attform->attinhcount > expected_parents)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("cannot rename inherited column \"%s\"",
@@ -3410,7 +3424,7 @@ ATSimpleRecursion(List **wqueue, Relation rel,
ListCell *child;
List *children;
- children = find_all_inheritors(relid, AccessExclusiveLock);
+ children = find_all_inheritors(relid, AccessExclusiveLock, NULL);
/*
* find_all_inheritors does the recursive search of the inheritance
@@ -7233,7 +7247,7 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent)
* We use weakest lock we can on child's children, namely AccessShareLock.
*/
children = find_all_inheritors(RelationGetRelid(child_rel),
- AccessShareLock);
+ AccessShareLock, NULL);
if (list_member_oid(children, RelationGetRelid(parent_rel)))
ereport(ERROR,
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index c7e9c85..50a6ab7 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1180,7 +1180,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
lockmode = AccessShareLock;
/* Scan for all members of inheritance set, acquire needed locks */
- inhOIDs = find_all_inheritors(parentOID, lockmode);
+ inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);
/*
* Check that there's at least one descendant, else treat as no-child
diff --git a/src/include/catalog/pg_inherits_fn.h b/src/include/catalog/pg_inherits_fn.h
index 91baec3..70a624b 100644
--- a/src/include/catalog/pg_inherits_fn.h
+++ b/src/include/catalog/pg_inherits_fn.h
@@ -18,7 +18,8 @@
#include "storage/lock.h"
extern List *find_inheritance_children(Oid parentrelId, LOCKMODE lockmode);
-extern List *find_all_inheritors(Oid parentrelId, LOCKMODE lockmode);
+extern List *find_all_inheritors(Oid parentrelId, LOCKMODE lockmode,
+ List **parents);
extern bool has_subclass(Oid relationId);
extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 8bc716a..f9269cc 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -43,7 +43,7 @@ extern void renameatt(Oid myrelid,
const char *oldattname,
const char *newattname,
bool recurse,
- bool recursing);
+ int expected_parents);
extern void RenameRelation(Oid myrelid,
const char *newrelname,
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index 9c83a32..dc54e56 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -1053,3 +1053,97 @@ 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 in 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 t1 RENAME a TO aa;
+ALTER TABLE t1 RENAME b TO bb; -- to be failed
+ERROR: cannot rename 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 | integer | | plain |
+ dd | integer | | plain |
+Inherits: t1,
+ s1
+Has OIDs: no
+
+DROP TABLE ts;
+-- Test for renaming in 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 RENAME aa TO aaa;
+\d+ t4
+ Table "public.t4"
+ Column | Type | Modifiers | Storage | Description
+--------+---------+-----------+---------+-------------
+ aaa | integer | | 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 RENAME aaa TO aaaa;
+ALTER TABLE t1 RENAME b TO bb; -- to be failed
+ERROR: cannot rename inherited column "b"
+\d+ ts
+ Table "public.ts"
+ Column | Type | Modifiers | Storage | Description
+--------+---------+-----------+---------+-------------
+ aaaa | integer | | plain |
+ b | integer | | plain |
+ x | integer | | plain |
+ c | integer | | plain |
+ d | integer | | plain |
+Inherits: t2,
+ s1
+Has OIDs: no
+
+WITH RECURSIVE r AS (
+ SELECT 't1'::regclass AS inhrelid
+UNION ALL
+ SELECT c.inhrelid FROM pg_inherits c, r WHERE r.inhrelid = c.inhparent
+)
+SELECT a.attrelid::regclass, a.attname, a.attinhcount, e.expected
+ FROM (SELECT inhrelid, count(*) AS expected FROM pg_inherits
+ WHERE inhparent IN (SELECT inhrelid FROM r) GROUP BY inhrelid) e
+ JOIN pg_attribute a ON e.inhrelid = a.attrelid WHERE NOT attislocal
+ ORDER BY a.attrelid, a.attnum;
+ attrelid | attname | attinhcount | expected
+----------+---------+-------------+----------
+ t2 | aaaa | 1 | 1
+ t2 | b | 1 | 1
+ t3 | aaaa | 1 | 1
+ t3 | b | 1 | 1
+ t4 | aaaa | 2 | 2
+ t4 | b | 2 | 2
+ t4 | x | 1 | 2
+ t4 | y | 1 | 2
+ ts | aaaa | 1 | 1
+ ts | b | 2 | 1
+ ts | x | 1 | 1
+ ts | c | 1 | 1
+(12 rows)
+
+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..bbd5752 100644
--- a/src/test/regress/sql/inherit.sql
+++ b/src/test/regress/sql/inherit.sql
@@ -334,3 +334,42 @@ 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 in 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 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 in 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 RENAME aa TO aaa;
+\d+ t4
+
+CREATE TABLE ts (d int) INHERITS (t2, s1);
+ALTER TABLE t1 RENAME aaa TO aaaa;
+ALTER TABLE t1 RENAME b TO bb; -- to be failed
+\d+ ts
+
+WITH RECURSIVE r AS (
+ SELECT 't1'::regclass AS inhrelid
+UNION ALL
+ SELECT c.inhrelid FROM pg_inherits c, r WHERE r.inhrelid = c.inhparent
+)
+SELECT a.attrelid::regclass, a.attname, a.attinhcount, e.expected
+ FROM (SELECT inhrelid, count(*) AS expected FROM pg_inherits
+ WHERE inhparent IN (SELECT inhrelid FROM r) GROUP BY inhrelid) e
+ JOIN pg_attribute a ON e.inhrelid = a.attrelid WHERE NOT attislocal
+ ORDER BY a.attrelid, a.attnum;
+
+DROP TABLE t1, s1 CASCADE;
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers