mirror of
https://github.com/postgres/postgres.git
synced 2025-12-12 02:37:31 +03:00
Fire non-deferred AFTER triggers immediately upon query completion,
rather than when returning to the idle loop. This makes no particular difference for interactively-issued queries, but it makes a big difference for queries issued within functions: trigger execution now occurs before the calling function is allowed to proceed. This responds to numerous complaints about nonintuitive behavior of foreign key checking, such as http://archives.postgresql.org/pgsql-bugs/2004-09/msg00020.php, and appears to be required by the SQL99 spec. Also take the opportunity to simplify the data structures used for the pending-trigger list, rename them for more clarity, and squeeze out a bit of space.
This commit is contained in:
@@ -646,6 +646,7 @@ SELECT * from FKTABLE;
|
||||
UPDATE PKTABLE set ptest2=5 where ptest2=2;
|
||||
ERROR: insert or update on table "fktable" violates foreign key constraint "constrname3"
|
||||
DETAIL: Key (ftest1,ftest2,ftest3)=(1,-1,3) is not present in table "pktable".
|
||||
CONTEXT: SQL query "UPDATE ONLY "public"."fktable" SET "ftest2" = DEFAULT WHERE "ftest1" = $1 AND "ftest2" = $2 AND "ftest3" = $3"
|
||||
-- Try to update something that will set default
|
||||
UPDATE PKTABLE set ptest1=0, ptest2=5, ptest3=10 where ptest2=2;
|
||||
UPDATE PKTABLE set ptest2=10 where ptest2=4;
|
||||
|
||||
@@ -1935,3 +1935,74 @@ select * from foo;
|
||||
20
|
||||
(2 rows)
|
||||
|
||||
--
|
||||
-- test foreign key error trapping
|
||||
--
|
||||
create temp table master(f1 int primary key);
|
||||
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "master_pkey" for table "master"
|
||||
create temp table slave(f1 int references master deferrable);
|
||||
insert into master values(1);
|
||||
insert into slave values(1);
|
||||
insert into slave values(2); -- fails
|
||||
ERROR: insert or update on table "slave" violates foreign key constraint "slave_f1_fkey"
|
||||
DETAIL: Key (f1)=(2) is not present in table "master".
|
||||
create function trap_foreign_key(int) returns int as $$
|
||||
begin
|
||||
begin -- start a subtransaction
|
||||
insert into slave values($1);
|
||||
exception
|
||||
when foreign_key_violation then
|
||||
raise notice 'caught foreign_key_violation';
|
||||
return 0;
|
||||
end;
|
||||
return 1;
|
||||
end$$ language plpgsql;
|
||||
create function trap_foreign_key_2() returns int as $$
|
||||
begin
|
||||
begin -- start a subtransaction
|
||||
set constraints all immediate;
|
||||
exception
|
||||
when foreign_key_violation then
|
||||
raise notice 'caught foreign_key_violation';
|
||||
return 0;
|
||||
end;
|
||||
return 1;
|
||||
end$$ language plpgsql;
|
||||
select trap_foreign_key(1);
|
||||
trap_foreign_key
|
||||
------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
select trap_foreign_key(2); -- detects FK violation
|
||||
NOTICE: caught foreign_key_violation
|
||||
trap_foreign_key
|
||||
------------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
begin;
|
||||
set constraints all deferred;
|
||||
select trap_foreign_key(2); -- should not detect FK violation
|
||||
trap_foreign_key
|
||||
------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
savepoint x;
|
||||
set constraints all immediate; -- fails
|
||||
ERROR: insert or update on table "slave" violates foreign key constraint "slave_f1_fkey"
|
||||
DETAIL: Key (f1)=(2) is not present in table "master".
|
||||
rollback to x;
|
||||
select trap_foreign_key_2(); -- detects FK violation
|
||||
NOTICE: caught foreign_key_violation
|
||||
trap_foreign_key_2
|
||||
--------------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
commit; -- still fails
|
||||
ERROR: insert or update on table "slave" violates foreign key constraint "slave_f1_fkey"
|
||||
DETAIL: Key (f1)=(2) is not present in table "master".
|
||||
drop function trap_foreign_key(int);
|
||||
drop function trap_foreign_key_2();
|
||||
|
||||
@@ -1699,3 +1699,54 @@ select blockme();
|
||||
reset statement_timeout;
|
||||
|
||||
select * from foo;
|
||||
|
||||
--
|
||||
-- test foreign key error trapping
|
||||
--
|
||||
|
||||
create temp table master(f1 int primary key);
|
||||
|
||||
create temp table slave(f1 int references master deferrable);
|
||||
|
||||
insert into master values(1);
|
||||
insert into slave values(1);
|
||||
insert into slave values(2); -- fails
|
||||
|
||||
create function trap_foreign_key(int) returns int as $$
|
||||
begin
|
||||
begin -- start a subtransaction
|
||||
insert into slave values($1);
|
||||
exception
|
||||
when foreign_key_violation then
|
||||
raise notice 'caught foreign_key_violation';
|
||||
return 0;
|
||||
end;
|
||||
return 1;
|
||||
end$$ language plpgsql;
|
||||
|
||||
create function trap_foreign_key_2() returns int as $$
|
||||
begin
|
||||
begin -- start a subtransaction
|
||||
set constraints all immediate;
|
||||
exception
|
||||
when foreign_key_violation then
|
||||
raise notice 'caught foreign_key_violation';
|
||||
return 0;
|
||||
end;
|
||||
return 1;
|
||||
end$$ language plpgsql;
|
||||
|
||||
select trap_foreign_key(1);
|
||||
select trap_foreign_key(2); -- detects FK violation
|
||||
|
||||
begin;
|
||||
set constraints all deferred;
|
||||
select trap_foreign_key(2); -- should not detect FK violation
|
||||
savepoint x;
|
||||
set constraints all immediate; -- fails
|
||||
rollback to x;
|
||||
select trap_foreign_key_2(); -- detects FK violation
|
||||
commit; -- still fails
|
||||
|
||||
drop function trap_foreign_key(int);
|
||||
drop function trap_foreign_key_2();
|
||||
|
||||
Reference in New Issue
Block a user