aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/catalog/pg_constraint.c12
-rw-r--r--src/backend/commands/tablecmds.c26
-rw-r--r--src/test/regress/expected/foreign_key.out20
-rw-r--r--src/test/regress/sql/foreign_key.sql16
4 files changed, 66 insertions, 8 deletions
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 446b54b9ffa..698b493fc40 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -783,6 +783,12 @@ ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId)
constrForm = (Form_pg_constraint) GETSTRUCT(newtup);
if (OidIsValid(parentConstrId))
{
+ /* don't allow setting parent for a constraint that already has one */
+ Assert(constrForm->coninhcount == 0);
+ if (constrForm->conparentid != InvalidOid)
+ elog(ERROR, "constraint %u already has a parent constraint",
+ childConstrId);
+
constrForm->conislocal = false;
constrForm->coninhcount++;
constrForm->conparentid = parentConstrId;
@@ -797,10 +803,12 @@ ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId)
else
{
constrForm->coninhcount--;
- if (constrForm->coninhcount <= 0)
- constrForm->conislocal = true;
+ constrForm->conislocal = true;
constrForm->conparentid = InvalidOid;
+ /* Make sure there's no further inheritance. */
+ Assert(constrForm->coninhcount == 0);
+
deleteDependencyRecordsForClass(ConstraintRelationId, childConstrId,
ConstraintRelationId,
DEPENDENCY_INTERNAL_AUTO);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 887c19c3eff..e010586dd67 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7243,6 +7243,7 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
Oid pfeqoperators[INDEX_MAX_KEYS];
Oid ppeqoperators[INDEX_MAX_KEYS];
Oid ffeqoperators[INDEX_MAX_KEYS];
+ bool connoinherit;
int i;
int numfks,
numpks;
@@ -7587,6 +7588,12 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
}
/*
+ * FKs always inherit for partitioned tables, and never for legacy
+ * inheritance.
+ */
+ connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
+
+ /*
* Record the FK constraint in pg_constraint.
*/
constrOid = CreateConstraintEntry(fkconstraint->conname,
@@ -7616,7 +7623,7 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
NULL,
true, /* islocal */
0, /* inhcount */
- true, /* isnoinherit */
+ connoinherit, /* conNoInherit */
false); /* is_internal */
ObjectAddressSet(address, ConstraintRelationId, constrOid);
@@ -7937,7 +7944,7 @@ CloneFkReferencing(Relation pg_constraint, Relation parentRel,
}
systable_endscan(scan);
- heap_close(trigrel, RowExclusiveLock);
+ table_close(trigrel, RowExclusiveLock);
ConstraintSetParentConstraint(fk->conoid, parentConstrOid);
CommandCounterIncrement();
@@ -9131,6 +9138,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
HeapTuple tuple;
bool found = false;
bool is_no_inherit_constraint = false;
+ char contype;
/* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing)
@@ -9171,6 +9179,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
constrName, RelationGetRelationName(rel))));
is_no_inherit_constraint = con->connoinherit;
+ contype = con->contype;
/*
* If it's a foreign-key constraint, we'd better lock the referenced
@@ -9179,7 +9188,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
* that has unfired events). But we can/must skip that in the
* self-referential case.
*/
- if (con->contype == CONSTRAINT_FOREIGN &&
+ if (contype == CONSTRAINT_FOREIGN &&
con->confrelid != RelationGetRelid(rel))
{
Relation frel;
@@ -9224,6 +9233,17 @@ ATExecDropConstraint(Relation rel, const char *constrName,
}
/*
+ * For partitioned tables, non-CHECK inherited constraints are dropped via
+ * the dependency mechanism, so we're done here.
+ */
+ if (contype != CONSTRAINT_CHECK &&
+ rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ {
+ table_close(conrel, RowExclusiveLock);
+ return;
+ }
+
+ /*
* Propagate to children as appropriate. Unlike most other ALTER
* routines, we have to do this one level of recursion at a time; we can't
* use find_all_inheritors to do it in one pass.
diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out
index 76040a76011..bf2c91d9f0e 100644
--- a/src/test/regress/expected/foreign_key.out
+++ b/src/test/regress/expected/foreign_key.out
@@ -1945,7 +1945,23 @@ DETAIL: Key (a)=(2) is not present in table "pkey".
delete from fkpart1.pkey where a = 1;
ERROR: update or delete on table "pkey" violates foreign key constraint "fk_part_a_fkey" on table "fk_part_1"
DETAIL: Key (a)=(1) is still referenced from table "fk_part_1".
+-- verify that attaching and detaching partitions manipulates the inheritance
+-- properties of their FK constraints correctly
+create schema fkpart2
+ create table pkey (a int primary key)
+ create table fk_part (a int, constraint fkey foreign key (a) references fkpart2.pkey) partition by list (a)
+ create table fk_part_1 partition of fkpart2.fk_part for values in (1) partition by list (a)
+ create table fk_part_1_1 (a int, constraint my_fkey foreign key (a) references fkpart2.pkey);
+alter table fkpart2.fk_part_1 attach partition fkpart2.fk_part_1_1 for values in (1);
+alter table fkpart2.fk_part_1 drop constraint fkey; -- should fail
+ERROR: cannot drop inherited constraint "fkey" of relation "fk_part_1"
+alter table fkpart2.fk_part_1_1 drop constraint my_fkey; -- should fail
+ERROR: cannot drop inherited constraint "my_fkey" of relation "fk_part_1_1"
+alter table fkpart2.fk_part detach partition fkpart2.fk_part_1;
+alter table fkpart2.fk_part_1 drop constraint fkey; -- ok
+alter table fkpart2.fk_part_1_1 drop constraint my_fkey; -- doesn't exist
+ERROR: constraint "my_fkey" of relation "fk_part_1_1" does not exist
\set VERBOSITY terse \\ -- suppress cascade details
-drop schema fkpart0, fkpart1 cascade;
-NOTICE: drop cascades to 5 other objects
+drop schema fkpart0, fkpart1, fkpart2 cascade;
+NOTICE: drop cascades to 8 other objects
\set VERBOSITY default
diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql
index 9ed1166c666..c8d1214d02c 100644
--- a/src/test/regress/sql/foreign_key.sql
+++ b/src/test/regress/sql/foreign_key.sql
@@ -1392,6 +1392,20 @@ create table fkpart1.fk_part_1_2 partition of fkpart1.fk_part_1 for values in (2
insert into fkpart1.fk_part_1 values (2); -- should fail
delete from fkpart1.pkey where a = 1;
+-- verify that attaching and detaching partitions manipulates the inheritance
+-- properties of their FK constraints correctly
+create schema fkpart2
+ create table pkey (a int primary key)
+ create table fk_part (a int, constraint fkey foreign key (a) references fkpart2.pkey) partition by list (a)
+ create table fk_part_1 partition of fkpart2.fk_part for values in (1) partition by list (a)
+ create table fk_part_1_1 (a int, constraint my_fkey foreign key (a) references fkpart2.pkey);
+alter table fkpart2.fk_part_1 attach partition fkpart2.fk_part_1_1 for values in (1);
+alter table fkpart2.fk_part_1 drop constraint fkey; -- should fail
+alter table fkpart2.fk_part_1_1 drop constraint my_fkey; -- should fail
+alter table fkpart2.fk_part detach partition fkpart2.fk_part_1;
+alter table fkpart2.fk_part_1 drop constraint fkey; -- ok
+alter table fkpart2.fk_part_1_1 drop constraint my_fkey; -- doesn't exist
+
\set VERBOSITY terse \\ -- suppress cascade details
-drop schema fkpart0, fkpart1 cascade;
+drop schema fkpart0, fkpart1, fkpart2 cascade;
\set VERBOSITY default