Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit6ba979c

Browse files
author
Álvaro Herrera
committed
Handle self-referencing FKs correctly in partitioned tables
For self-referencing foreign keys in partitioned tables, we weren'thandling creation of pg_constraint rows during CREATE TABLE PARTITION ASas well as ALTER TABLE ATTACH PARTITION. This is an old bug -- mostly,we broke this in614a406 while trying to fix it (so 12.13, 13.9,14.6 and 15.0 and up all behave incorrectly). This commit reverts partof that with additional fixes for full correctness, and installs moretests to verify the parts we broke, not just the catalog contents butalso the user-visible behavior.Backpatch to all live branches. In branches 13 and 14, commit46a8c27 changed the behavior during DETACH to drop a FKconstraint rather than trying to repair it, because the complete fix ofrepairing catalog constraints was problematic due to lack of previousfixes. For this reason, the test behavior in those branches is a bitdifferent. However, as best as I can tell, the fix works correctlythere.In release notes we have to recommend that all self-referencing foreignkeys on partitioned tables be recreated if partitions have been createdor attached after the FK was created, keeping in mind that violatingrows might already be present on the referencing side.Reported-by: Guillaume Lelarge <guillaume@lelarge.info>Reported-by: Matthew Gabeler-Lee <fastcat@gmail.com>Reported-by: Luca Vallisa <luca.vallisa@gmail.com>Discussion:https://postgr.es/m/CAECtzeWHCA+6tTcm2Oh2+g7fURUJpLZb-=pRXgeWJ-Pi+VU=_w@mail.gmail.comDiscussion:https://postgr.es/m/18156-a44bc7096f0683e6@postgresql.orgDiscussion:https://postgr.es/m/CAAT=myvsiF-Attja5DcWoUWh21R12R-sfXECY2-3ynt8kaOqjw@mail.gmail.com
1 parent8ae4ac6 commit6ba979c

File tree

4 files changed

+107
-61
lines changed

4 files changed

+107
-61
lines changed

‎src/backend/commands/tablecmds.c

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10199,14 +10199,14 @@ CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
1019910199
Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
1020010200

1020110201
/*
10202-
*Cloneconstraintsfor whichthe parent is on thereferenced side.
10202+
*First, cloneconstraintswherethe parent is on thereferencing side.
1020310203
*/
10204-
CloneFkReferenced(parentRel, partitionRel);
10204+
CloneFkReferencing(wqueue,parentRel, partitionRel);
1020510205

1020610206
/*
10207-
*Now cloneconstraintswherethe parent is on thereferencing side.
10207+
*Cloneconstraintsfor whichthe parent is on thereferenced side.
1020810208
*/
10209-
CloneFkReferencing(wqueue,parentRel, partitionRel);
10209+
CloneFkReferenced(parentRel, partitionRel);
1021010210
}
1021110211

1021210212
/*
@@ -10217,8 +10217,6 @@ CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
1021710217
* clone those constraints to the given partition. This is to be called
1021810218
* when the partition is being created or attached.
1021910219
*
10220-
* This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10221-
*
1022210220
* This recurses to partitions, if the relation being attached is partitioned.
1022310221
* Recursion is done by calling addFkRecurseReferenced.
1022410222
*/
@@ -10308,17 +10306,6 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel)
1030810306
continue;
1030910307
}
1031010308

10311-
/*
10312-
* Don't clone self-referencing foreign keys, which can be in the
10313-
* partitioned table or in the partition-to-be.
10314-
*/
10315-
if (constrForm->conrelid == RelationGetRelid(parentRel) ||
10316-
constrForm->conrelid == RelationGetRelid(partitionRel))
10317-
{
10318-
ReleaseSysCache(tuple);
10319-
continue;
10320-
}
10321-
1032210309
/* We need the same lock level that CreateTrigger will acquire */
1032310310
fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
1032410311

‎src/test/regress/expected/foreign_key.out

Lines changed: 68 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2042,58 +2042,90 @@ CREATE TABLE part33_self_fk (
20422042
id_abc bigint
20432043
);
20442044
ALTER TABLE part3_self_fk ATTACH PARTITION part33_self_fk FOR VALUES FROM (30) TO (40);
2045-
SELECT cr.relname, co.conname, co.contype, co.convalidated,
2045+
-- verify that this constraint works
2046+
INSERT INTO parted_self_fk VALUES (1, NULL), (2, NULL), (3, NULL);
2047+
INSERT INTO parted_self_fk VALUES (10, 1), (11, 2), (12, 3) RETURNING tableoid::regclass;
2048+
tableoid
2049+
---------------
2050+
part2_self_fk
2051+
part2_self_fk
2052+
part2_self_fk
2053+
(3 rows)
2054+
2055+
INSERT INTO parted_self_fk VALUES (4, 5);-- error: referenced doesn't exist
2056+
ERROR: insert or update on table "part1_self_fk" violates foreign key constraint "parted_self_fk_id_abc_fkey"
2057+
DETAIL: Key (id_abc)=(5) is not present in table "parted_self_fk".
2058+
DELETE FROM parted_self_fk WHERE id = 1 RETURNING *;-- error: reference remains
2059+
ERROR: update or delete on table "part1_self_fk" violates foreign key constraint "parted_self_fk_id_abc_fkey1" on table "parted_self_fk"
2060+
DETAIL: Key (id)=(1) is still referenced from table "parted_self_fk".
2061+
SELECT cr.relname, co.conname, co.convalidated,
20462062
p.conname AS conparent, p.convalidated, cf.relname AS foreignrel
20472063
FROM pg_constraint co
20482064
JOIN pg_class cr ON cr.oid = co.conrelid
20492065
LEFT JOIN pg_class cf ON cf.oid = co.confrelid
20502066
LEFT JOIN pg_constraint p ON p.oid = co.conparentid
2051-
WHEREcr.oid IN (SELECT relid FROM pg_partition_tree('parted_self_fk'))
2052-
ORDER BY co.contype,cr.relname, co.conname, p.conname;
2053-
relname |conname | contype | convalidated | conparent | convalidated | foreignrel
2054-
----------------+----------------------------+---------+--------------+----------------------------+--------------+----------------
2055-
part1_self_fk | parted_self_fk_id_abc_fkey | f | t | parted_self_fk_id_abc_fkey | t | parted_self_fk
2056-
part2_self_fk | parted_self_fk_id_abc_fkey| f| t | parted_self_fk_id_abc_fkey | t | parted_self_fk
2057-
part32_self_fk| parted_self_fk_id_abc_fkey| f| t | parted_self_fk_id_abc_fkey | t | parted_self_fk
2058-
part33_self_fk | parted_self_fk_id_abc_fkey| f| t | parted_self_fk_id_abc_fkey | t | parted_self_fk
2059-
part3_self_fk| parted_self_fk_id_abc_fkey| f| t | parted_self_fk_id_abc_fkey | t | parted_self_fk
2060-
parted_self_fk| parted_self_fk_id_abc_fkey| f| t | | | parted_self_fk
2061-
part1_self_fk | part1_self_fk_pkey| p| t| parted_self_fk_pkey| t|
2062-
part2_self_fk | part2_self_fk_pkey | p |t |parted_self_fk_pkey| t |
2063-
part32_self_fk |part32_self_fk_pkey | p |t |part3_self_fk_pkey| t |
2064-
part33_self_fk |part33_self_fk_pkey | p |t |part3_self_fk_pkey| t |
2065-
part3_self_fk | part3_self_fk_pkey | p |t |parted_self_fk_pkey| t |
2066-
parted_self_fk |parted_self_fk_pkey | p |t | | |
2067-
(12 rows)
2067+
WHEREco.contype = 'f' AND
2068+
cr.oid IN (SELECT relid FROM pg_partition_tree('parted_self_fk'))
2069+
ORDER BY cr.relname, co.conname, p.conname;
2070+
relname | conname | convalidated | conparent | convalidated | foreignrel
2071+
----------------+-----------------------------+--------------+-----------------------------+--------------+----------------
2072+
part1_self_fk | parted_self_fk_id_abc_fkey| t | parted_self_fk_id_abc_fkey | t | parted_self_fk
2073+
part2_self_fk| parted_self_fk_id_abc_fkey| t | parted_self_fk_id_abc_fkey | t | parted_self_fk
2074+
part32_self_fk | parted_self_fk_id_abc_fkey| t | parted_self_fk_id_abc_fkey | t | parted_self_fk
2075+
part33_self_fk| parted_self_fk_id_abc_fkey| t | parted_self_fk_id_abc_fkey | t | parted_self_fk
2076+
part3_self_fk| parted_self_fk_id_abc_fkey| t |parted_self_fk_id_abc_fkey | t | parted_self_fk
2077+
parted_self_fk | parted_self_fk_id_abc_fkey | t | | | parted_self_fk
2078+
parted_self_fk | parted_self_fk_id_abc_fkey1 |t |parted_self_fk_id_abc_fkey| t |part1_self_fk
2079+
parted_self_fk |parted_self_fk_id_abc_fkey2 |t |parted_self_fk_id_abc_fkey| t |part2_self_fk
2080+
parted_self_fk |parted_self_fk_id_abc_fkey3 |t |parted_self_fk_id_abc_fkey| t |part3_self_fk
2081+
parted_self_fk | parted_self_fk_id_abc_fkey4 |t |parted_self_fk_id_abc_fkey3| t |part32_self_fk
2082+
parted_self_fk |parted_self_fk_id_abc_fkey5 |t |parted_self_fk_id_abc_fkey3 | t| part33_self_fk
2083+
(11 rows)
20682084

20692085
-- detach and re-attach multiple times just to ensure everything is kosher
20702086
ALTER TABLE parted_self_fk DETACH PARTITION part2_self_fk;
2087+
INSERT INTO part2_self_fk VALUES (16, 9);-- error: referenced doesn't exist
2088+
ERROR: insert or update on table "part2_self_fk" violates foreign key constraint "parted_self_fk_id_abc_fkey"
2089+
DETAIL: Key (id_abc)=(9) is not present in table "parted_self_fk".
2090+
DELETE FROM parted_self_fk WHERE id = 2 RETURNING *;-- error: reference remains
2091+
ERROR: update or delete on table "part1_self_fk" violates foreign key constraint "part2_self_fk_id_abc_fkey" on table "part2_self_fk"
2092+
DETAIL: Key (id)=(2) is still referenced from table "part2_self_fk".
20712093
ALTER TABLE parted_self_fk ATTACH PARTITION part2_self_fk FOR VALUES FROM (10) TO (20);
2094+
INSERT INTO parted_self_fk VALUES (16, 9);-- error: referenced doesn't exist
2095+
ERROR: insert or update on table "part2_self_fk" violates foreign key constraint "parted_self_fk_id_abc_fkey"
2096+
DETAIL: Key (id_abc)=(9) is not present in table "parted_self_fk".
2097+
DELETE FROM parted_self_fk WHERE id = 3 RETURNING *;-- error: reference remains
2098+
ERROR: update or delete on table "part1_self_fk" violates foreign key constraint "parted_self_fk_id_abc_fkey1" on table "parted_self_fk"
2099+
DETAIL: Key (id)=(3) is still referenced from table "parted_self_fk".
20722100
ALTER TABLE parted_self_fk DETACH PARTITION part2_self_fk;
20732101
ALTER TABLE parted_self_fk ATTACH PARTITION part2_self_fk FOR VALUES FROM (10) TO (20);
2074-
SELECT cr.relname, co.conname, co.contype, co.convalidated,
2102+
ALTER TABLE parted_self_fk DETACH PARTITION part3_self_fk;
2103+
ALTER TABLE parted_self_fk ATTACH PARTITION part3_self_fk FOR VALUES FROM (30) TO (40);
2104+
ALTER TABLE part3_self_fk DETACH PARTITION part33_self_fk;
2105+
ALTER TABLE part3_self_fk ATTACH PARTITION part33_self_fk FOR VALUES FROM (30) TO (40);
2106+
SELECT cr.relname, co.conname, co.convalidated,
20752107
p.conname AS conparent, p.convalidated, cf.relname AS foreignrel
20762108
FROM pg_constraint co
20772109
JOIN pg_class cr ON cr.oid = co.conrelid
20782110
LEFT JOIN pg_class cf ON cf.oid = co.confrelid
20792111
LEFT JOIN pg_constraint p ON p.oid = co.conparentid
2080-
WHEREcr.oid IN (SELECT relid FROM pg_partition_tree('parted_self_fk'))
2081-
ORDER BY co.contype,cr.relname, co.conname, p.conname;
2082-
relname |conname | contype | convalidated | conparent | convalidated | foreignrel
2083-
----------------+----------------------------+---------+--------------+----------------------------+--------------+----------------
2084-
part1_self_fk | parted_self_fk_id_abc_fkey | f | t | parted_self_fk_id_abc_fkey | t | parted_self_fk
2085-
part2_self_fk | parted_self_fk_id_abc_fkey| f| t | parted_self_fk_id_abc_fkey | t | parted_self_fk
2086-
part32_self_fk| parted_self_fk_id_abc_fkey| f| t | parted_self_fk_id_abc_fkey | t | parted_self_fk
2087-
part33_self_fk | parted_self_fk_id_abc_fkey| f| t | parted_self_fk_id_abc_fkey | t | parted_self_fk
2088-
part3_self_fk| parted_self_fk_id_abc_fkey| f| t | parted_self_fk_id_abc_fkey | t | parted_self_fk
2089-
parted_self_fk| parted_self_fk_id_abc_fkey| f| t | | | parted_self_fk
2090-
part1_self_fk | part1_self_fk_pkey| p| t| parted_self_fk_pkey| t|
2091-
part2_self_fk | part2_self_fk_pkey | p |t |parted_self_fk_pkey| t |
2092-
part32_self_fk |part32_self_fk_pkey | p |t |part3_self_fk_pkey| t |
2093-
part33_self_fk |part33_self_fk_pkey | p |t |part3_self_fk_pkey| t |
2094-
part3_self_fk | part3_self_fk_pkey | p |t |parted_self_fk_pkey| t |
2095-
parted_self_fk |parted_self_fk_pkey | p |t | | |
2096-
(12 rows)
2112+
WHEREco.contype = 'f' AND
2113+
cr.oid IN (SELECT relid FROM pg_partition_tree('parted_self_fk'))
2114+
ORDER BY cr.relname, co.conname, p.conname;
2115+
relname | conname | convalidated | conparent | convalidated | foreignrel
2116+
----------------+-----------------------------+--------------+-----------------------------+--------------+----------------
2117+
part1_self_fk | parted_self_fk_id_abc_fkey| t | parted_self_fk_id_abc_fkey | t | parted_self_fk
2118+
part2_self_fk| parted_self_fk_id_abc_fkey| t | parted_self_fk_id_abc_fkey | t | parted_self_fk
2119+
part32_self_fk | parted_self_fk_id_abc_fkey| t | parted_self_fk_id_abc_fkey | t | parted_self_fk
2120+
part33_self_fk| parted_self_fk_id_abc_fkey| t | parted_self_fk_id_abc_fkey | t | parted_self_fk
2121+
part3_self_fk| parted_self_fk_id_abc_fkey| t |parted_self_fk_id_abc_fkey | t | parted_self_fk
2122+
parted_self_fk | parted_self_fk_id_abc_fkey | t | | | parted_self_fk
2123+
parted_self_fk | parted_self_fk_id_abc_fkey1 |t |parted_self_fk_id_abc_fkey| t |part1_self_fk
2124+
parted_self_fk |parted_self_fk_id_abc_fkey2 |t |parted_self_fk_id_abc_fkey| t |part2_self_fk
2125+
parted_self_fk |parted_self_fk_id_abc_fkey3 |t |parted_self_fk_id_abc_fkey| t |part3_self_fk
2126+
parted_self_fk | parted_self_fk_id_abc_fkey4 |t |parted_self_fk_id_abc_fkey3| t |part32_self_fk
2127+
parted_self_fk |parted_self_fk_id_abc_fkey5 |t |parted_self_fk_id_abc_fkey3 | t| part33_self_fk
2128+
(11 rows)
20972129

20982130
-- Leave this table around, for pg_upgrade/pg_dump tests
20992131
-- Test creating a constraint at the parent that already exists in partitions.

‎src/test/regress/expected/triggers.out

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2796,11 +2796,13 @@ select tgrelid::regclass, rtrim(tgname, '0123456789') as tgname,
27962796
---------+-------------------------+------------------------+-----------
27972797
child1 | RI_ConstraintTrigger_c_ | "RI_FKey_check_ins" | O
27982798
child1 | RI_ConstraintTrigger_c_ | "RI_FKey_check_upd" | O
2799+
child1 | RI_ConstraintTrigger_a_ | "RI_FKey_noaction_del" | O
2800+
child1 | RI_ConstraintTrigger_a_ | "RI_FKey_noaction_upd" | O
27992801
parent | RI_ConstraintTrigger_c_ | "RI_FKey_check_ins" | O
28002802
parent | RI_ConstraintTrigger_c_ | "RI_FKey_check_upd" | O
28012803
parent | RI_ConstraintTrigger_a_ | "RI_FKey_noaction_del" | O
28022804
parent | RI_ConstraintTrigger_a_ | "RI_FKey_noaction_upd" | O
2803-
(6 rows)
2805+
(8 rows)
28042806

28052807
alter table parent disable trigger all;
28062808
select tgrelid::regclass, rtrim(tgname, '0123456789') as tgname,
@@ -2811,11 +2813,13 @@ select tgrelid::regclass, rtrim(tgname, '0123456789') as tgname,
28112813
---------+-------------------------+------------------------+-----------
28122814
child1 | RI_ConstraintTrigger_c_ | "RI_FKey_check_ins" | D
28132815
child1 | RI_ConstraintTrigger_c_ | "RI_FKey_check_upd" | D
2816+
child1 | RI_ConstraintTrigger_a_ | "RI_FKey_noaction_del" | D
2817+
child1 | RI_ConstraintTrigger_a_ | "RI_FKey_noaction_upd" | D
28142818
parent | RI_ConstraintTrigger_c_ | "RI_FKey_check_ins" | D
28152819
parent | RI_ConstraintTrigger_c_ | "RI_FKey_check_upd" | D
28162820
parent | RI_ConstraintTrigger_a_ | "RI_FKey_noaction_del" | D
28172821
parent | RI_ConstraintTrigger_a_ | "RI_FKey_noaction_upd" | D
2818-
(6 rows)
2822+
(8 rows)
28192823

28202824
drop table parent, child1;
28212825
-- Verify that firing state propagates correctly on creation, too

‎src/test/regress/sql/foreign_key.sql

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,29 +1490,52 @@ CREATE TABLE part33_self_fk (
14901490
);
14911491
ALTERTABLE part3_self_fk ATTACH PARTITION part33_self_fk FORVALUESFROM (30) TO (40);
14921492

1493-
SELECTcr.relname,co.conname,co.contype,co.convalidated,
1493+
-- verify that this constraint works
1494+
INSERT INTO parted_self_fkVALUES (1,NULL), (2,NULL), (3,NULL);
1495+
INSERT INTO parted_self_fkVALUES (10,1), (11,2), (12,3) RETURNING tableoid::regclass;
1496+
1497+
INSERT INTO parted_self_fkVALUES (4,5);-- error: referenced doesn't exist
1498+
DELETEFROM parted_self_fkWHERE id=1 RETURNING*;-- error: reference remains
1499+
1500+
SELECTcr.relname,co.conname,co.convalidated,
14941501
p.connameAS conparent,p.convalidated,cf.relnameAS foreignrel
14951502
FROM pg_constraint co
14961503
JOIN pg_class crONcr.oid=co.conrelid
14971504
LEFT JOIN pg_class cfONcf.oid=co.confrelid
14981505
LEFT JOIN pg_constraint pONp.oid=co.conparentid
1499-
WHEREcr.oidIN (SELECT relidFROM pg_partition_tree('parted_self_fk'))
1500-
ORDER BYco.contype,cr.relname,co.conname,p.conname;
1506+
WHEREco.contype='f'AND
1507+
cr.oidIN (SELECT relidFROM pg_partition_tree('parted_self_fk'))
1508+
ORDER BYcr.relname,co.conname,p.conname;
15011509

15021510
-- detach and re-attach multiple times just to ensure everything is kosher
15031511
ALTERTABLE parted_self_fk DETACH PARTITION part2_self_fk;
1512+
1513+
INSERT INTO part2_self_fkVALUES (16,9);-- error: referenced doesn't exist
1514+
DELETEFROM parted_self_fkWHERE id=2 RETURNING*;-- error: reference remains
1515+
15041516
ALTERTABLE parted_self_fk ATTACH PARTITION part2_self_fk FORVALUESFROM (10) TO (20);
1517+
1518+
INSERT INTO parted_self_fkVALUES (16,9);-- error: referenced doesn't exist
1519+
DELETEFROM parted_self_fkWHERE id=3 RETURNING*;-- error: reference remains
1520+
15051521
ALTERTABLE parted_self_fk DETACH PARTITION part2_self_fk;
15061522
ALTERTABLE parted_self_fk ATTACH PARTITION part2_self_fk FORVALUESFROM (10) TO (20);
15071523

1508-
SELECTcr.relname,co.conname,co.contype,co.convalidated,
1524+
ALTERTABLE parted_self_fk DETACH PARTITION part3_self_fk;
1525+
ALTERTABLE parted_self_fk ATTACH PARTITION part3_self_fk FORVALUESFROM (30) TO (40);
1526+
1527+
ALTERTABLE part3_self_fk DETACH PARTITION part33_self_fk;
1528+
ALTERTABLE part3_self_fk ATTACH PARTITION part33_self_fk FORVALUESFROM (30) TO (40);
1529+
1530+
SELECTcr.relname,co.conname,co.convalidated,
15091531
p.connameAS conparent,p.convalidated,cf.relnameAS foreignrel
15101532
FROM pg_constraint co
15111533
JOIN pg_class crONcr.oid=co.conrelid
15121534
LEFT JOIN pg_class cfONcf.oid=co.confrelid
15131535
LEFT JOIN pg_constraint pONp.oid=co.conparentid
1514-
WHEREcr.oidIN (SELECT relidFROM pg_partition_tree('parted_self_fk'))
1515-
ORDER BYco.contype,cr.relname,co.conname,p.conname;
1536+
WHEREco.contype='f'AND
1537+
cr.oidIN (SELECT relidFROM pg_partition_tree('parted_self_fk'))
1538+
ORDER BYcr.relname,co.conname,p.conname;
15161539

15171540
-- Leave this table around, for pg_upgrade/pg_dump tests
15181541

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp