Commit b4bcc6bf authored by Alvaro Herrera's avatar Alvaro Herrera

Fix SET CONSTRAINTS .. DEFERRED on partitioned tables

SET CONSTRAINTS ... DEFERRED failed on partitioned tables, because of a
sanity check that ensures that the affected constraints have triggers.
On partitioned tables, the triggers are in the leaf partitions, not in
the partitioned relations themselves, so the sanity check fails.
Removing the sanity check solves the problem, because the code needed to
support the case is already there.

Backpatch to 11.

Note: deferred unique constraints are not affected by this bug, because
they do have triggers in the parent partitioned table.  I did not add a
test for this scenario.

Discussion: https://postgr.es/m/20191105212915.GA11324@alvherre.pgsql
parent a7145f6b
...@@ -5510,13 +5510,10 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt) ...@@ -5510,13 +5510,10 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
foreach(lc, conoidlist) foreach(lc, conoidlist)
{ {
Oid conoid = lfirst_oid(lc); Oid conoid = lfirst_oid(lc);
bool found;
ScanKeyData skey; ScanKeyData skey;
SysScanDesc tgscan; SysScanDesc tgscan;
HeapTuple htup; HeapTuple htup;
found = false;
ScanKeyInit(&skey, ScanKeyInit(&skey,
Anum_pg_trigger_tgconstraint, Anum_pg_trigger_tgconstraint,
BTEqualStrategyNumber, F_OIDEQ, BTEqualStrategyNumber, F_OIDEQ,
...@@ -5537,16 +5534,9 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt) ...@@ -5537,16 +5534,9 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
*/ */
if (pg_trigger->tgdeferrable) if (pg_trigger->tgdeferrable)
tgoidlist = lappend_oid(tgoidlist, pg_trigger->oid); tgoidlist = lappend_oid(tgoidlist, pg_trigger->oid);
found = true;
} }
systable_endscan(tgscan); systable_endscan(tgscan);
/* Safety check: a deferrable constraint should have triggers */
if (!found)
elog(ERROR, "no triggers found for constraint with OID %u",
conoid);
} }
table_close(tgrel, AccessShareLock); table_close(tgrel, AccessShareLock);
......
...@@ -2040,9 +2040,28 @@ alter table fkpart2.fk_part detach partition fkpart2.fk_part_1; ...@@ -2040,9 +2040,28 @@ 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 drop constraint fkey; -- ok
alter table fkpart2.fk_part_1_1 drop constraint my_fkey; -- doesn't exist 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 ERROR: constraint "my_fkey" of relation "fk_part_1_1" does not exist
drop schema fkpart0, fkpart1, fkpart2 cascade; -- verify constraint deferrability
NOTICE: drop cascades to 8 other objects create schema fkpart3
DETAIL: drop cascades to table fkpart2.pkey create table pkey (a int primary key)
create table fk_part (a int, constraint fkey foreign key (a) references fkpart3.pkey deferrable initially immediate) partition by list (a)
create table fk_part_1 partition of fkpart3.fk_part for values in (1) partition by list (a)
create table fk_part_1_1 partition of fkpart3.fk_part_1 for values in (1)
create table fk_part_2 partition of fkpart3.fk_part for values in (2);
begin;
set constraints fkpart3.fkey deferred;
insert into fkpart3.fk_part values (1);
insert into fkpart3.pkey values (1);
commit;
begin;
set constraints fkpart3.fkey deferred;
delete from fkpart3.pkey;
delete from fkpart3.fk_part;
commit;
drop schema fkpart0, fkpart1, fkpart2, fkpart3 cascade;
NOTICE: drop cascades to 10 other objects
DETAIL: drop cascades to table fkpart3.pkey
drop cascades to table fkpart3.fk_part
drop cascades to table fkpart2.pkey
drop cascades to table fkpart2.fk_part drop cascades to table fkpart2.fk_part
drop cascades to table fkpart2.fk_part_1 drop cascades to table fkpart2.fk_part_1
drop cascades to table fkpart1.pkey drop cascades to table fkpart1.pkey
...@@ -2267,6 +2286,37 @@ INSERT INTO fk4 VALUES (50); ...@@ -2267,6 +2286,37 @@ INSERT INTO fk4 VALUES (50);
ALTER TABLE fk ATTACH PARTITION fk4 FOR VALUES IN (50); ALTER TABLE fk ATTACH PARTITION fk4 FOR VALUES IN (50);
ERROR: insert or update on table "fk4" violates foreign key constraint "fk_a_fkey" ERROR: insert or update on table "fk4" violates foreign key constraint "fk_a_fkey"
DETAIL: Key (a)=(50) is not present in table "pk". DETAIL: Key (a)=(50) is not present in table "pk".
-- Verify constraint deferrability
CREATE SCHEMA fkpart9;
SET search_path TO fkpart9;
CREATE TABLE pk (a int PRIMARY KEY) PARTITION BY LIST (a);
CREATE TABLE pk1 PARTITION OF pk FOR VALUES IN (1, 2) PARTITION BY LIST (a);
CREATE TABLE pk11 PARTITION OF pk1 FOR VALUES IN (1);
CREATE TABLE pk3 PARTITION OF pk FOR VALUES IN (3);
CREATE TABLE fk (a int REFERENCES pk DEFERRABLE INITIALLY IMMEDIATE);
INSERT INTO fk VALUES (1); -- should fail
ERROR: insert or update on table "fk" violates foreign key constraint "fk_a_fkey"
DETAIL: Key (a)=(1) is not present in table "pk".
BEGIN;
SET CONSTRAINTS fk_a_fkey DEFERRED;
INSERT INTO fk VALUES (1);
COMMIT; -- should fail
ERROR: insert or update on table "fk" violates foreign key constraint "fk_a_fkey"
DETAIL: Key (a)=(1) is not present in table "pk".
BEGIN;
SET CONSTRAINTS fk_a_fkey DEFERRED;
INSERT INTO fk VALUES (1);
INSERT INTO pk VALUES (1);
COMMIT; -- OK
BEGIN;
SET CONSTRAINTS fk_a_fkey DEFERRED;
DELETE FROM pk WHERE a = 1;
DELETE FROM fk WHERE a = 1;
COMMIT; -- OK
DROP SCHEMA fkpart9 CASCADE;
NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to table pk
drop cascades to table fk
-- Verify ON UPDATE/DELETE behavior -- Verify ON UPDATE/DELETE behavior
CREATE SCHEMA fkpart6; CREATE SCHEMA fkpart6;
SET search_path TO fkpart6; SET search_path TO fkpart6;
......
...@@ -1455,7 +1455,25 @@ alter table fkpart2.fk_part detach partition fkpart2.fk_part_1; ...@@ -1455,7 +1455,25 @@ 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 drop constraint fkey; -- ok
alter table fkpart2.fk_part_1_1 drop constraint my_fkey; -- doesn't exist alter table fkpart2.fk_part_1_1 drop constraint my_fkey; -- doesn't exist
drop schema fkpart0, fkpart1, fkpart2 cascade; -- verify constraint deferrability
create schema fkpart3
create table pkey (a int primary key)
create table fk_part (a int, constraint fkey foreign key (a) references fkpart3.pkey deferrable initially immediate) partition by list (a)
create table fk_part_1 partition of fkpart3.fk_part for values in (1) partition by list (a)
create table fk_part_1_1 partition of fkpart3.fk_part_1 for values in (1)
create table fk_part_2 partition of fkpart3.fk_part for values in (2);
begin;
set constraints fkpart3.fkey deferred;
insert into fkpart3.fk_part values (1);
insert into fkpart3.pkey values (1);
commit;
begin;
set constraints fkpart3.fkey deferred;
delete from fkpart3.pkey;
delete from fkpart3.fk_part;
commit;
drop schema fkpart0, fkpart1, fkpart2, fkpart3 cascade;
-- Test a partitioned table as referenced table. -- Test a partitioned table as referenced table.
...@@ -1595,6 +1613,31 @@ CREATE TABLE fk4 (LIKE fk); ...@@ -1595,6 +1613,31 @@ CREATE TABLE fk4 (LIKE fk);
INSERT INTO fk4 VALUES (50); INSERT INTO fk4 VALUES (50);
ALTER TABLE fk ATTACH PARTITION fk4 FOR VALUES IN (50); ALTER TABLE fk ATTACH PARTITION fk4 FOR VALUES IN (50);
-- Verify constraint deferrability
CREATE SCHEMA fkpart9;
SET search_path TO fkpart9;
CREATE TABLE pk (a int PRIMARY KEY) PARTITION BY LIST (a);
CREATE TABLE pk1 PARTITION OF pk FOR VALUES IN (1, 2) PARTITION BY LIST (a);
CREATE TABLE pk11 PARTITION OF pk1 FOR VALUES IN (1);
CREATE TABLE pk3 PARTITION OF pk FOR VALUES IN (3);
CREATE TABLE fk (a int REFERENCES pk DEFERRABLE INITIALLY IMMEDIATE);
INSERT INTO fk VALUES (1); -- should fail
BEGIN;
SET CONSTRAINTS fk_a_fkey DEFERRED;
INSERT INTO fk VALUES (1);
COMMIT; -- should fail
BEGIN;
SET CONSTRAINTS fk_a_fkey DEFERRED;
INSERT INTO fk VALUES (1);
INSERT INTO pk VALUES (1);
COMMIT; -- OK
BEGIN;
SET CONSTRAINTS fk_a_fkey DEFERRED;
DELETE FROM pk WHERE a = 1;
DELETE FROM fk WHERE a = 1;
COMMIT; -- OK
DROP SCHEMA fkpart9 CASCADE;
-- Verify ON UPDATE/DELETE behavior -- Verify ON UPDATE/DELETE behavior
CREATE SCHEMA fkpart6; CREATE SCHEMA fkpart6;
SET search_path TO fkpart6; SET search_path TO fkpart6;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment