pgsql-fix-inherit-rename.7.patch
application/octect-stream
Filename: pgsql-fix-inherit-rename.7.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 | 81 | 0 |
| src/backend/commands/alter.c | 1 | 0 |
| src/backend/commands/analyze.c | 2 | 0 |
| src/backend/commands/tablecmds.c | 32 | 0 |
| src/backend/optimizer/prep/prepunion.c | 1 | 0 |
| src/include/catalog/pg_inherits_fn.h | 2 | 1 |
| src/include/commands/tablecmds.h | 1 | 0 |
| src/test/regress/expected/inherit.out | 93 | 0 |
| src/test/regress/sql/inherit.sql | 38 | 0 |
*** a/src/backend/catalog/pg_inherits.c
--- b/src/backend/catalog/pg_inherits.c
***************
*** 148,163 **** 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.
*
* 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
* then no locks are acquired, but caller must beware of race conditions
* against possible DROPs of child relations.
*/
! List *
! find_all_inheritors(Oid parentrelId, LOCKMODE lockmode)
{
! List *rels_list;
ListCell *l;
/*
--- 148,194 ----
* find_all_inheritors -
* Returns a list of relation OIDs including the given rel plus
* all relations that inherit from it, directly or indirectly.
+ * In addition, it also returns the expected inhcount for them.
*
* 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
* then no locks are acquired, but caller must beware of race conditions
* against possible DROPs of child relations.
+ *
+ * The expected inhcount means the number of times a certain relation being
+ * inherited from any other relations which were inherited from the root
+ * of inheritance tree directly or indirectly.
+ *
+ * It is used to control some of ALTER TABLE operations on columns inherited
+ * from a relation. For example, an inherited column must has same name
+ * within the series of inheritance. So, we need to prevent renaming when
+ * the column to be renamed is delivered from multiple independent relations.
+ *
+ * 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
+ * then no locks are acquired, but caller must beware of race conditions
+ * against possible DROPs of child relations.
+ *
+ * T2 For example, T2 and T3 are inherited from T1, T4 is inherited
+ * / \ from T2 and T3. And, T5 is inherited from T3 and S1.
+ * T1 T4 When we try to rename a column within T1, it needs to rename
+ * \ / inherited column in the child relations also.
+ * T3-T5 If attinhcount of the column is larger than its expected inhcount,
+ * / it means the column is delivered from the different origin, so
+ * S1 we have to prevent this operations to keep database integrity.
+ *
+ * In this example, the expected inhcount is 1 in T2, T3 and T5, and 2 in T4.
+ * The column inherited from T1 has attinhcount = 2 in T4, so it does not
+ * prevent renaming.
+ *
+ * If the column inherited from T1 has attinhcount = 2 in T5, it means the
+ * column was also inherited from S1, so we can prevent renaming it.
*/
! void
! find_all_inheritors(Oid parentrelId, LOCKMODE lockmode,
! List **child_oids, List **child_inhs)
{
! List *rel_oids, *rel_inhs;
ListCell *l;
/*
***************
*** 167,193 **** find_all_inheritors(Oid parentrelId, LOCKMODE lockmode)
* children. This is a bit tricky but works because the foreach() macro
* doesn't fetch the next list element until the bottom of the loop.
*/
! rels_list = list_make1_oid(parentrelId);
! foreach(l, rels_list)
{
! Oid currentrel = lfirst_oid(l);
! List *currentchildren;
/* Get the direct children of this rel */
! currentchildren = find_inheritance_children(currentrel, lockmode);
! /*
! * 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.)
! */
! rels_list = list_concat_unique_oid(rels_list, currentchildren);
}
! return rels_list;
}
--- 198,257 ----
* children. This is a bit tricky but works because the foreach() macro
* doesn't fetch the next list element until the bottom of the loop.
*/
! rel_oids = list_make1_oid(parentrelId);
! rel_inhs = list_make1_int(0);
! foreach(l, rel_oids)
{
! Oid cur_relid = lfirst_oid(l);
! Oid temp_relid;
! List *temp;
! ListCell *lt, *lo, *li;
/* Get the direct children of this rel */
! temp = find_inheritance_children(cur_relid, lockmode);
! foreach(lt, temp)
! {
! Oid temp_relid = lfirst_oid(lt);
! bool found = false;
!
! /*
! * 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.)
! */
! forboth(lo, rel_oids, li, rel_inhs)
! {
! /*
! * Increment its expected inhcount, if the child relation
! * is already chained to the rel_oids due to the diamond
! * inheritance tree. In this case, we don't need to chain
! * a new entry into the list.
! */
!
! if (lfirst_oid(lo) == temp_relid)
! {
! lfirst_int(li)++;
! found = true;
! break;
! }
! }
!
! if (!found)
! {
! rel_oids = lappend_oid(rel_oids, temp_relid);
! rel_inhs = lappend_int(rel_inhs, 1);
! }
! }
}
! if (child_oids)
! *child_oids = rel_oids;
! if (child_inhs)
! *child_inhs = rel_inhs;
}
*** a/src/backend/commands/alter.c
--- b/src/backend/commands/alter.c
***************
*** 125,130 **** ExecRenameStmt(RenameStmt *stmt)
--- 125,131 ----
renameatt(relid,
stmt->subname, /* old att name */
stmt->newname, /* new att name */
+ 0, /* expected inhcount */
interpretInhOption(stmt->relation->inhOpt), /* recursive? */
false); /* recursing already? */
break;
*** a/src/backend/commands/analyze.c
--- b/src/backend/commands/analyze.c
***************
*** 1390,1396 **** 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);
/*
* Check that there's at least one descendant, else fail. This could
--- 1390,1397 ----
* Find all members of inheritance set. We only need AccessShareLock on
* the children.
*/
! find_all_inheritors(RelationGetRelid(onerel), AccessShareLock,
! &tableOIDs, NULL);
/*
* Check that there's at least one descendant, else fail. This could
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 824,830 **** ExecuteTruncate(TruncateStmt *stmt)
ListCell *child;
List *children;
! children = find_all_inheritors(myrelid, AccessExclusiveLock);
foreach(child, children)
{
--- 824,831 ----
ListCell *child;
List *children;
! find_all_inheritors(myrelid, AccessExclusiveLock,
! &children, NULL);
foreach(child, children)
{
***************
*** 1942,1947 **** void
--- 1943,1949 ----
renameatt(Oid myrelid,
const char *oldattname,
const char *newattname,
+ int expected_inhcount,
bool recurse,
bool recursing)
{
***************
*** 1987,2010 **** renameatt(Oid myrelid,
*/
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
--- 1989,2019 ----
*/
if (recurse)
{
! List *child_oids, *child_inhs;
! ListCell *lo, *li;
/*
* 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.
+ * Also, we need the list of expected inhcount, since it has to be
+ * forbidden to rename a column that is inherited from different
+ * origin. (Note that we allow diamond-inheritance tree)
*/
! find_all_inheritors(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
***************
*** 2048,2053 **** renameatt(Oid myrelid,
--- 2057,2073 ----
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 */
***************
*** 3410,3416 **** ATSimpleRecursion(List **wqueue, Relation rel,
ListCell *child;
List *children;
! children = find_all_inheritors(relid, AccessExclusiveLock);
/*
* find_all_inheritors does the recursive search of the inheritance
--- 3430,3436 ----
ListCell *child;
List *children;
! find_all_inheritors(relid, AccessExclusiveLock, &children, NULL);
/*
* find_all_inheritors does the recursive search of the inheritance
***************
*** 7232,7239 **** 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);
if (list_member_oid(children, RelationGetRelid(parent_rel)))
ereport(ERROR,
--- 7252,7259 ----
*
* We use weakest lock we can on child's children, namely AccessShareLock.
*/
! find_all_inheritors(RelationGetRelid(child_rel), AccessShareLock,
! &children, NULL);
if (list_member_oid(children, RelationGetRelid(parent_rel)))
ereport(ERROR,
*** a/src/backend/optimizer/prep/prepunion.c
--- b/src/backend/optimizer/prep/prepunion.c
***************
*** 1180,1186 **** 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);
/*
* Check that there's at least one descendant, else treat as no-child
--- 1180,1186 ----
lockmode = AccessShareLock;
/* Scan for all members of inheritance set, acquire needed locks */
! find_all_inheritors(parentOID, lockmode, &inhOIDs, NULL);
/*
* Check that there's at least one descendant, else treat as no-child
*** a/src/include/catalog/pg_inherits_fn.h
--- b/src/include/catalog/pg_inherits_fn.h
***************
*** 18,24 ****
#include "storage/lock.h"
extern List *find_inheritance_children(Oid parentrelId, LOCKMODE lockmode);
! extern List *find_all_inheritors(Oid parentrelId, LOCKMODE lockmode);
extern bool has_subclass(Oid relationId);
extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);
--- 18,25 ----
#include "storage/lock.h"
extern List *find_inheritance_children(Oid parentrelId, LOCKMODE lockmode);
! extern void find_all_inheritors(Oid parentrelId, LOCKMODE lockmode,
! List **child_oids, List **child_inhs);
extern bool has_subclass(Oid relationId);
extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);
*** a/src/include/commands/tablecmds.h
--- b/src/include/commands/tablecmds.h
***************
*** 42,47 **** extern void ExecuteTruncate(TruncateStmt *stmt);
--- 42,48 ----
extern void renameatt(Oid myrelid,
const char *oldattname,
const char *newattname,
+ int expected_inhcount,
bool recurse,
bool recursing);
*** a/src/test/regress/expected/inherit.out
--- b/src/test/regress/expected/inherit.out
***************
*** 1053,1055 **** NOTICE: merging column "a" with inherited definition
--- 1053,1148 ----
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 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 | 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 multiple 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 attrelid::regclass, attname, attinhcount, r.expected
+ FROM (SELECT inhrelid, count(*) AS expected FROM pg_inherits
+ WHERE inhparent IN (SELECT inhrelid FROM r) GROUP BY inhrelid) r
+ JOIN pg_attribute a ON r.inhrelid = a.attrelid WHERE attislocal = false;
+ attrelid | attname | attinhcount | expected
+ ----------+---------+-------------+----------
+ t2 | b | 1 | 1
+ t3 | b | 1 | 1
+ t4 | b | 2 | 2
+ t4 | x | 1 | 2
+ t4 | y | 1 | 2
+ ts | b | 2 | 1
+ ts | x | 1 | 1
+ ts | c | 1 | 1
+ t2 | aaaa | 1 | 1
+ t3 | aaaa | 1 | 1
+ t4 | aaaa | 2 | 2
+ ts | aaaa | 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
*** a/src/test/regress/sql/inherit.sql
--- b/src/test/regress/sql/inherit.sql
***************
*** 334,336 **** CREATE TABLE inh_error1 () INHERITS (t1, t4);
--- 334,374 ----
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 attrelid::regclass, attname, attinhcount, r.expected
+ FROM (SELECT inhrelid, count(*) AS expected FROM pg_inherits
+ WHERE inhparent IN (SELECT inhrelid FROM r) GROUP BY inhrelid) r
+ JOIN pg_attribute a ON r.inhrelid = a.attrelid WHERE attislocal = false;
+
+ DROP TABLE t1, s1 CASCADE;