Skip to content

Commit 936ab6d

Browse files
committed
Fix some more bugs in foreign keys connecting partitioned tables
* In DetachPartitionFinalize() we were applying a tuple conversion map to tuples that didn't need one, which can lead to erratic behavior if a partitioned table has a partition with a different column order, as reported by Alexander Lakhin. This was introduced by 53af949. Don't do that. Also, modify a recently added test case to exercise this. * The same function as well as CloneFkReferenced() were acquiring AccessShareLock on a partition, only to have CreateTrigger() later acquire ShareRowExclusiveLock on it. This can lead to deadlock by lock escalation, unnecessarily. Avoid that by acquiring the stronger lock to begin with. This probably dates back to branch 12, but I have never seen a report of this being a problem in the field. * Innocuous but wasteful: also introduced by 53af949, we were reading a pg_constraint tuple from syscache that we don't need, as reported by Tender Wang. Don't. Backpatch to 15. Discussion: https://postgr.es/m/461e9c26-2076-8224-e119-84998b6a784e@gmail.com
1 parent 9aef6f1 commit 936ab6d

File tree

3 files changed

+27
-30
lines changed

3 files changed

+27
-30
lines changed

src/backend/commands/tablecmds.c

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10251,6 +10251,9 @@ addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
1025110251
Oid deleteTriggerOid,
1025210252
updateTriggerOid;
1025310253

10254+
Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10255+
Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10256+
1025410257
/*
1025510258
* Create the action triggers that enforce the constraint.
1025610259
*/
@@ -10277,6 +10280,7 @@ addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
1027710280
Oid partIndexId;
1027810281
ObjectAddress address;
1027910282

10283+
/* XXX would it be better to acquire these locks beforehand? */
1028010284
partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
1028110285

1028210286
/*
@@ -10379,6 +10383,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
1037910383
updateTriggerOid;
1038010384

1038110385
Assert(OidIsValid(parentConstr));
10386+
Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10387+
Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
1038210388

1038310389
if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1038410390
ereport(ERROR,
@@ -10669,13 +10675,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel)
1066910675
continue;
1067010676
}
1067110677

10672-
/*
10673-
* Because we're only expanding the key space at the referenced side,
10674-
* we don't need to prevent any operation in the referencing table, so
10675-
* AccessShareLock suffices (assumes that dropping the constraint
10676-
* acquires AccessExclusiveLock).
10677-
*/
10678-
fkRel = table_open(constrForm->conrelid, AccessShareLock);
10678+
/* We need the same lock level that CreateTrigger will acquire */
10679+
fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
1067910680

1068010681
indexOid = constrForm->conindid;
1068110682
DeconstructFkConstraintRow(tuple,
@@ -19280,8 +19281,7 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
1928019281
foreach(cell, fks)
1928119282
{
1928219283
ForeignKeyCacheInfo *fk = lfirst(cell);
19283-
HeapTuple contup,
19284-
parentConTup;
19284+
HeapTuple contup;
1928519285
Form_pg_constraint conform;
1928619286
Oid insertTriggerOid,
1928719287
updateTriggerOid;
@@ -19299,13 +19299,6 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
1929919299
continue;
1930019300
}
1930119301

19302-
Assert(OidIsValid(conform->conparentid));
19303-
parentConTup = SearchSysCache1(CONSTROID,
19304-
ObjectIdGetDatum(conform->conparentid));
19305-
if (!HeapTupleIsValid(parentConTup))
19306-
elog(ERROR, "cache lookup failed for constraint %u",
19307-
conform->conparentid);
19308-
1930919302
/*
1931019303
* The constraint on this table must be marked no longer a child of
1931119304
* the parent's constraint, as do its check triggers.
@@ -19346,7 +19339,6 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
1934619339
Oid conffeqop[INDEX_MAX_KEYS];
1934719340
int numfkdelsetcols;
1934819341
AttrNumber confdelsetcols[INDEX_MAX_KEYS];
19349-
AttrMap *attmap;
1935019342
Relation refdRel;
1935119343

1935219344
DeconstructFkConstraintRow(contup,
@@ -19379,20 +19371,19 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
1937919371
fkconstraint->old_pktable_oid = InvalidOid;
1938019372
fkconstraint->location = -1;
1938119373

19382-
attmap = build_attrmap_by_name(RelationGetDescr(partRel),
19383-
RelationGetDescr(rel),
19384-
false);
19374+
/* set up colnames, used to generate the constraint name */
1938519375
for (int i = 0; i < numfks; i++)
1938619376
{
1938719377
Form_pg_attribute att;
1938819378

1938919379
att = TupleDescAttr(RelationGetDescr(partRel),
19390-
attmap->attnums[conkey[i] - 1] - 1);
19380+
conkey[i] - 1);
19381+
1939119382
fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
1939219383
makeString(NameStr(att->attname)));
1939319384
}
1939419385

19395-
refdRel = table_open(fk->confrelid, AccessShareLock);
19386+
refdRel = table_open(fk->confrelid, ShareRowExclusiveLock);
1939619387

1939719388
addFkRecurseReferenced(fkconstraint, partRel,
1939819389
refdRel,
@@ -19408,11 +19399,10 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
1940819399
confdelsetcols,
1940919400
true,
1941019401
InvalidOid, InvalidOid);
19411-
table_close(refdRel, AccessShareLock);
19402+
table_close(refdRel, NoLock); /* keep lock till end of xact */
1941219403
}
1941319404

1941419405
ReleaseSysCache(contup);
19415-
ReleaseSysCache(parentConTup);
1941619406
}
1941719407
list_free_deep(fks);
1941819408
if (trigrel)

src/test/regress/expected/foreign_key.out

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2944,17 +2944,20 @@ CREATE SCHEMA fkpart12
29442944
CREATE TABLE fk_p ( id int, jd int, PRIMARY KEY(id, jd)) PARTITION BY list (id)
29452945
CREATE TABLE fk_p_1 PARTITION OF fk_p FOR VALUES IN (1) PARTITION BY list (jd)
29462946
CREATE TABLE fk_p_1_1 PARTITION OF fk_p_1 FOR VALUES IN (1)
2947-
CREATE TABLE fk_p_1_2 PARTITION OF fk_p_1 FOR VALUES IN (2)
2947+
CREATE TABLE fk_p_1_2 (x int, y int, jd int NOT NULL, id int NOT NULL)
29482948
CREATE TABLE fk_p_2 PARTITION OF fk_p FOR VALUES IN (2) PARTITION BY list (jd)
29492949
CREATE TABLE fk_p_2_1 PARTITION OF fk_p_2 FOR VALUES IN (1)
29502950
CREATE TABLE fk_p_2_2 PARTITION OF fk_p_2 FOR VALUES IN (2)
2951-
CREATE TABLE fk_r_1 ( id int PRIMARY KEY, p_id int NOT NULL, p_jd int NOT NULL)
2951+
CREATE TABLE fk_r_1 ( p_jd int NOT NULL, x int, id int PRIMARY KEY, p_id int NOT NULL)
29522952
CREATE TABLE fk_r_2 ( id int PRIMARY KEY, p_id int NOT NULL, p_jd int NOT NULL) PARTITION BY list (id)
29532953
CREATE TABLE fk_r_2_1 PARTITION OF fk_r_2 FOR VALUES IN (2, 1)
29542954
CREATE TABLE fk_r ( id int PRIMARY KEY, p_id int NOT NULL, p_jd int NOT NULL,
29552955
FOREIGN KEY (p_id, p_jd) REFERENCES fk_p (id, jd)
29562956
) PARTITION BY list (id);
29572957
SET search_path TO fkpart12;
2958+
ALTER TABLE fk_p_1_2 DROP COLUMN x, DROP COLUMN y;
2959+
ALTER TABLE fk_p_1 ATTACH PARTITION fk_p_1_2 FOR VALUES IN (2);
2960+
ALTER TABLE fk_r_1 DROP COLUMN x;
29582961
INSERT INTO fk_p VALUES (1, 1);
29592962
ALTER TABLE fk_r ATTACH PARTITION fk_r_1 FOR VALUES IN (1);
29602963
ALTER TABLE fk_r ATTACH PARTITION fk_r_2 FOR VALUES IN (2);
@@ -2993,7 +2996,7 @@ Foreign-key constraints:
29932996
"fk_r_p_id_p_jd_fkey" FOREIGN KEY (p_id, p_jd) REFERENCES fk_p(id, jd)
29942997
Number of partitions: 1 (Use \d+ to list them.)
29952998

2996-
INSERT INTO fk_r_1 VALUES (2, 1, 2); -- should fail
2999+
INSERT INTO fk_r_1 (id, p_id, p_jd) VALUES (2, 1, 2); -- should fail
29973000
ERROR: insert or update on table "fk_r_1" violates foreign key constraint "fk_r_p_id_p_jd_fkey"
29983001
DETAIL: Key (p_id, p_jd)=(1, 2) is not present in table "fk_p".
29993002
DELETE FROM fk_p; -- should fail

src/test/regress/sql/foreign_key.sql

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2097,18 +2097,22 @@ CREATE SCHEMA fkpart12
20972097
CREATE TABLE fk_p ( id int, jd int, PRIMARY KEY(id, jd)) PARTITION BY list (id)
20982098
CREATE TABLE fk_p_1 PARTITION OF fk_p FOR VALUES IN (1) PARTITION BY list (jd)
20992099
CREATE TABLE fk_p_1_1 PARTITION OF fk_p_1 FOR VALUES IN (1)
2100-
CREATE TABLE fk_p_1_2 PARTITION OF fk_p_1 FOR VALUES IN (2)
2100+
CREATE TABLE fk_p_1_2 (x int, y int, jd int NOT NULL, id int NOT NULL)
21012101
CREATE TABLE fk_p_2 PARTITION OF fk_p FOR VALUES IN (2) PARTITION BY list (jd)
21022102
CREATE TABLE fk_p_2_1 PARTITION OF fk_p_2 FOR VALUES IN (1)
21032103
CREATE TABLE fk_p_2_2 PARTITION OF fk_p_2 FOR VALUES IN (2)
2104-
CREATE TABLE fk_r_1 ( id int PRIMARY KEY, p_id int NOT NULL, p_jd int NOT NULL)
2104+
CREATE TABLE fk_r_1 ( p_jd int NOT NULL, x int, id int PRIMARY KEY, p_id int NOT NULL)
21052105
CREATE TABLE fk_r_2 ( id int PRIMARY KEY, p_id int NOT NULL, p_jd int NOT NULL) PARTITION BY list (id)
21062106
CREATE TABLE fk_r_2_1 PARTITION OF fk_r_2 FOR VALUES IN (2, 1)
21072107
CREATE TABLE fk_r ( id int PRIMARY KEY, p_id int NOT NULL, p_jd int NOT NULL,
21082108
FOREIGN KEY (p_id, p_jd) REFERENCES fk_p (id, jd)
21092109
) PARTITION BY list (id);
21102110
SET search_path TO fkpart12;
21112111

2112+
ALTER TABLE fk_p_1_2 DROP COLUMN x, DROP COLUMN y;
2113+
ALTER TABLE fk_p_1 ATTACH PARTITION fk_p_1_2 FOR VALUES IN (2);
2114+
ALTER TABLE fk_r_1 DROP COLUMN x;
2115+
21122116
INSERT INTO fk_p VALUES (1, 1);
21132117

21142118
ALTER TABLE fk_r ATTACH PARTITION fk_r_1 FOR VALUES IN (1);
@@ -2124,7 +2128,7 @@ ALTER TABLE fk_r DETACH PARTITION fk_r_2;
21242128

21252129
\d fk_r_2
21262130

2127-
INSERT INTO fk_r_1 VALUES (2, 1, 2); -- should fail
2131+
INSERT INTO fk_r_1 (id, p_id, p_jd) VALUES (2, 1, 2); -- should fail
21282132
DELETE FROM fk_p; -- should fail
21292133

21302134
ALTER TABLE fk_r ATTACH PARTITION fk_r_1 FOR VALUES IN (1);

0 commit comments

Comments
 (0)