diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 4e3e04d077b..390abe6f1f3 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -4617,13 +4617,6 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, /* Make changes-so-far visible */ CommandCounterIncrement(); - /* - * Build and execute a CREATE CONSTRAINT TRIGGER statement for the CHECK - * action for both INSERTs and UPDATEs on the referencing table. - */ - CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, true); - CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, false); - /* * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON * DELETE action on the referenced table. @@ -4725,6 +4718,27 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, fk_trigger->args = NIL; (void) CreateTrigger(fk_trigger, constraintOid); + + /* Make changes-so-far visible */ + CommandCounterIncrement(); + + /* + * Build and execute CREATE CONSTRAINT TRIGGER statements for the CHECK + * action for both INSERTs and UPDATEs on the referencing table. + * + * Note: for a self-referential FK (referencing and referenced tables are + * the same), it is important that the ON UPDATE action fires before the + * CHECK action, since both triggers will fire on the same row during an + * UPDATE event; otherwise the CHECK trigger will be checking a non-final + * state of the row. Because triggers fire in name order, we are + * effectively relying on the OIDs of the triggers to sort correctly as + * text. This will work except when the OID counter wraps around or adds + * a digit, eg "99999" sorts after "100000". That is infrequent enough, + * and the use of self-referential FKs is rare enough, that we live with + * it for now. There will be a real fix in PG 9.2. + */ + CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, true); + CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, false); } /* diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index e24ceb51a87..fb3061e19a1 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -1297,3 +1297,35 @@ SELECT * FROM tasks; (3 rows) COMMIT; +-- +-- Test self-referential FK with CASCADE (bug #6268) +-- +create temp table selfref ( + a int primary key, + b int, + foreign key (b) references selfref (a) + on update cascade on delete cascade +); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "selfref_pkey" for table "selfref" +insert into selfref (a, b) +values + (0, 0), + (1, 1); +begin; + update selfref set a = 123 where a = 0; + select a, b from selfref; + a | b +-----+----- + 1 | 1 + 123 | 123 +(2 rows) + + update selfref set a = 456 where a = 123; + select a, b from selfref; + a | b +-----+----- + 1 | 1 + 456 | 456 +(2 rows) + +commit; diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql index 519a5056af0..5ae8b88cd3d 100644 --- a/src/test/regress/sql/foreign_key.sql +++ b/src/test/regress/sql/foreign_key.sql @@ -921,3 +921,25 @@ SELECT * FROM tasks; DELETE FROM users WHERE id = 2; SELECT * FROM tasks; COMMIT; + +-- +-- Test self-referential FK with CASCADE (bug #6268) +-- +create temp table selfref ( + a int primary key, + b int, + foreign key (b) references selfref (a) + on update cascade on delete cascade +); + +insert into selfref (a, b) +values + (0, 0), + (1, 1); + +begin; + update selfref set a = 123 where a = 0; + select a, b from selfref; + update selfref set a = 456 where a = 123; + select a, b from selfref; +commit;