1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-26 12:21:12 +03:00

Avoid bogus scans of partitions when marking FKs enforced

Similar to commit cc733ed164: when an unenforced foreign key that
references a partitioned table is altered to be enforced, we scan
the constrained table based on each partition on the referenced
partitioned table.  This is bogus and likely to cause the ALTER TABLE to
fail: we must only scan the constrained table as pointing to the
top-level partitioned table.  Oversight in commit eec0040c4b.  Fix by
eliding those scans.

Author: Amul Sul <sulamul@gmail.com>
Reported-by: jian he <jian.universality@gmail.com>
Discussion: https://postgr.es/m/CACJufxF1e_gPOLtsDoaE4VCgQPC8KZW_kPAjPR5Rvv4Ew=fb2A@mail.gmail.com
This commit is contained in:
Álvaro Herrera
2025-06-05 18:39:06 +02:00
parent 04acad82b0
commit e6f98d8848
3 changed files with 37 additions and 22 deletions

View File

@ -12466,9 +12466,12 @@ ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
/*
* Tell Phase 3 to check that the constraint is satisfied by existing
* rows.
* rows. Only applies to leaf partitions, and (for constraints that
* reference a partitioned table) only if this is not one of the
* pg_constraint rows that exist solely to support action triggers.
*/
if (rel->rd_rel->relkind == RELKIND_RELATION)
if (rel->rd_rel->relkind == RELKIND_RELATION &&
currcon->confrelid == pkrelid)
{
AlteredTableInfo *tab;
NewConstraint *newcon;

View File

@ -1895,8 +1895,8 @@ WHERE conrelid::regclass::text like 'fk_partitioned_fk%' ORDER BY oid::regclass:
(5 rows)
DROP TABLE fk_partitioned_fk, fk_notpartitioned_pk;
-- NOT VALID foreign key on a non-partitioned table referencing a partitioned
-- table
-- NOT VALID and NOT ENFORCED foreign key on a non-partitioned table
-- referencing a partitioned table
CREATE TABLE fk_partitioned_pk (a int, b int, PRIMARY KEY (a, b)) PARTITION BY RANGE (a, b);
CREATE TABLE fk_partitioned_pk_1 PARTITION OF fk_partitioned_pk FOR VALUES FROM (0,0) TO (1000,1000);
CREATE TABLE fk_partitioned_pk_2 PARTITION OF fk_partitioned_pk FOR VALUES FROM (1000,1000) TO (2000,2000);
@ -1905,26 +1905,35 @@ INSERT INTO fk_partitioned_pk VALUES(100,100), (1000,1000);
INSERT INTO fk_notpartitioned_fk VALUES(100,100), (1000,1000);
ALTER TABLE fk_notpartitioned_fk ADD CONSTRAINT fk_notpartitioned_fk_a_b_fkey
FOREIGN KEY (a, b) REFERENCES fk_partitioned_pk NOT VALID;
-- All constraints will be invalid.
ALTER TABLE fk_notpartitioned_fk ADD CONSTRAINT fk_notpartitioned_fk_a_b_fkey2
FOREIGN KEY (a, b) REFERENCES fk_partitioned_pk NOT ENFORCED;
-- All constraints will be invalid, and _fkey2 constraints will not be enforced.
SELECT conname, conenforced, convalidated FROM pg_constraint
WHERE conrelid = 'fk_notpartitioned_fk'::regclass ORDER BY oid::regclass::text;
conname | conenforced | convalidated
---------------------------------+-------------+--------------
fk_notpartitioned_fk_a_b_fkey | t | f
fk_notpartitioned_fk_a_b_fkey_1 | t | f
fk_notpartitioned_fk_a_b_fkey_2 | t | f
(3 rows)
conname | conenforced | convalidated
----------------------------------+-------------+--------------
fk_notpartitioned_fk_a_b_fkey | t | f
fk_notpartitioned_fk_a_b_fkey_1 | t | f
fk_notpartitioned_fk_a_b_fkey_2 | t | f
fk_notpartitioned_fk_a_b_fkey2 | f | f
fk_notpartitioned_fk_a_b_fkey2_1 | f | f
fk_notpartitioned_fk_a_b_fkey2_2 | f | f
(6 rows)
ALTER TABLE fk_notpartitioned_fk VALIDATE CONSTRAINT fk_notpartitioned_fk_a_b_fkey;
-- All constraints are now valid.
ALTER TABLE fk_notpartitioned_fk ALTER CONSTRAINT fk_notpartitioned_fk_a_b_fkey2 ENFORCED;
-- All constraints are now valid and enforced.
SELECT conname, conenforced, convalidated FROM pg_constraint
WHERE conrelid = 'fk_notpartitioned_fk'::regclass ORDER BY oid::regclass::text;
conname | conenforced | convalidated
---------------------------------+-------------+--------------
fk_notpartitioned_fk_a_b_fkey | t | t
fk_notpartitioned_fk_a_b_fkey_1 | t | t
fk_notpartitioned_fk_a_b_fkey_2 | t | t
(3 rows)
conname | conenforced | convalidated
----------------------------------+-------------+--------------
fk_notpartitioned_fk_a_b_fkey | t | t
fk_notpartitioned_fk_a_b_fkey_1 | t | t
fk_notpartitioned_fk_a_b_fkey_2 | t | t
fk_notpartitioned_fk_a_b_fkey2 | t | t
fk_notpartitioned_fk_a_b_fkey2_1 | t | t
fk_notpartitioned_fk_a_b_fkey2_2 | t | t
(6 rows)
-- test a self-referential FK
ALTER TABLE fk_partitioned_pk ADD CONSTRAINT selffk FOREIGN KEY (a, b) REFERENCES fk_partitioned_pk NOT VALID;

View File

@ -1389,8 +1389,8 @@ WHERE conrelid::regclass::text like 'fk_partitioned_fk%' ORDER BY oid::regclass:
DROP TABLE fk_partitioned_fk, fk_notpartitioned_pk;
-- NOT VALID foreign key on a non-partitioned table referencing a partitioned
-- table
-- NOT VALID and NOT ENFORCED foreign key on a non-partitioned table
-- referencing a partitioned table
CREATE TABLE fk_partitioned_pk (a int, b int, PRIMARY KEY (a, b)) PARTITION BY RANGE (a, b);
CREATE TABLE fk_partitioned_pk_1 PARTITION OF fk_partitioned_pk FOR VALUES FROM (0,0) TO (1000,1000);
CREATE TABLE fk_partitioned_pk_2 PARTITION OF fk_partitioned_pk FOR VALUES FROM (1000,1000) TO (2000,2000);
@ -1399,14 +1399,17 @@ INSERT INTO fk_partitioned_pk VALUES(100,100), (1000,1000);
INSERT INTO fk_notpartitioned_fk VALUES(100,100), (1000,1000);
ALTER TABLE fk_notpartitioned_fk ADD CONSTRAINT fk_notpartitioned_fk_a_b_fkey
FOREIGN KEY (a, b) REFERENCES fk_partitioned_pk NOT VALID;
ALTER TABLE fk_notpartitioned_fk ADD CONSTRAINT fk_notpartitioned_fk_a_b_fkey2
FOREIGN KEY (a, b) REFERENCES fk_partitioned_pk NOT ENFORCED;
-- All constraints will be invalid.
-- All constraints will be invalid, and _fkey2 constraints will not be enforced.
SELECT conname, conenforced, convalidated FROM pg_constraint
WHERE conrelid = 'fk_notpartitioned_fk'::regclass ORDER BY oid::regclass::text;
ALTER TABLE fk_notpartitioned_fk VALIDATE CONSTRAINT fk_notpartitioned_fk_a_b_fkey;
ALTER TABLE fk_notpartitioned_fk ALTER CONSTRAINT fk_notpartitioned_fk_a_b_fkey2 ENFORCED;
-- All constraints are now valid.
-- All constraints are now valid and enforced.
SELECT conname, conenforced, convalidated FROM pg_constraint
WHERE conrelid = 'fk_notpartitioned_fk'::regclass ORDER BY oid::regclass::text;