mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-12 18:34:36 +08:00
Fix incorrect optimization of foreign-key checks. When an UPDATE on the
referencing table does not change the tuple's FK column(s), we don't bother to check the PK table since the constraint was presumably already valid. However, the check is still necessary if the tuple was inserted by our own transaction, since in that case the INSERT trigger will conclude it need not make the check (since its version of the tuple has been deleted). We got this right for simple cases, but not when the insert and update are in different subtransactions of the current top-level transaction; in such cases the FK check would never be made at all. (Hence, problem dates back to 8.0 when subtransactions were added --- it's actually the subtransaction version of a bug fixed in 7.3.5.) Fix, and add regression test cases. Report and fix by Affan Salman.
This commit is contained in:
parent
a5ca334a21
commit
2c535bfe81
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.215 2007/07/01 17:45:42 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.216 2007/07/17 17:45:28 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -3396,8 +3396,7 @@ AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger,
|
||||
* anything, so we have to do the check for the UPDATE
|
||||
* anyway.
|
||||
*/
|
||||
if (HeapTupleHeaderGetXmin(oldtup->t_data) !=
|
||||
GetCurrentTransactionId() &&
|
||||
if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(oldtup->t_data)) &&
|
||||
RI_FKey_keyequal_upd_fk(trigger, rel, oldtup, newtup))
|
||||
{
|
||||
continue;
|
||||
|
@ -1193,3 +1193,40 @@ UPDATE fktable SET id = id + 1;
|
||||
COMMIT;
|
||||
ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_fk_fkey"
|
||||
DETAIL: Key (fk)=(20) is not present in table "pktable".
|
||||
-- check same case when insert is in a different subtransaction than update
|
||||
BEGIN;
|
||||
-- doesn't match PK, but no error yet
|
||||
INSERT INTO fktable VALUES (0, 20);
|
||||
-- UPDATE will be in a subxact
|
||||
SAVEPOINT savept1;
|
||||
-- don't change FK
|
||||
UPDATE fktable SET id = id + 1;
|
||||
-- should catch error from initial INSERT
|
||||
COMMIT;
|
||||
ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_fk_fkey"
|
||||
DETAIL: Key (fk)=(20) is not present in table "pktable".
|
||||
BEGIN;
|
||||
-- INSERT will be in a subxact
|
||||
SAVEPOINT savept1;
|
||||
-- doesn't match PK, but no error yet
|
||||
INSERT INTO fktable VALUES (0, 20);
|
||||
RELEASE SAVEPOINT savept1;
|
||||
-- don't change FK
|
||||
UPDATE fktable SET id = id + 1;
|
||||
-- should catch error from initial INSERT
|
||||
COMMIT;
|
||||
ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_fk_fkey"
|
||||
DETAIL: Key (fk)=(20) is not present in table "pktable".
|
||||
BEGIN;
|
||||
-- doesn't match PK, but no error yet
|
||||
INSERT INTO fktable VALUES (0, 20);
|
||||
-- UPDATE will be in a subxact
|
||||
SAVEPOINT savept1;
|
||||
-- don't change FK
|
||||
UPDATE fktable SET id = id + 1;
|
||||
-- Roll back the UPDATE
|
||||
ROLLBACK TO savept1;
|
||||
-- should catch error from initial INSERT
|
||||
COMMIT;
|
||||
ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_fk_fkey"
|
||||
DETAIL: Key (fk)=(20) is not present in table "pktable".
|
||||
|
@ -830,3 +830,52 @@ UPDATE fktable SET id = id + 1;
|
||||
|
||||
-- should catch error from initial INSERT
|
||||
COMMIT;
|
||||
|
||||
-- check same case when insert is in a different subtransaction than update
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- doesn't match PK, but no error yet
|
||||
INSERT INTO fktable VALUES (0, 20);
|
||||
|
||||
-- UPDATE will be in a subxact
|
||||
SAVEPOINT savept1;
|
||||
|
||||
-- don't change FK
|
||||
UPDATE fktable SET id = id + 1;
|
||||
|
||||
-- should catch error from initial INSERT
|
||||
COMMIT;
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- INSERT will be in a subxact
|
||||
SAVEPOINT savept1;
|
||||
|
||||
-- doesn't match PK, but no error yet
|
||||
INSERT INTO fktable VALUES (0, 20);
|
||||
|
||||
RELEASE SAVEPOINT savept1;
|
||||
|
||||
-- don't change FK
|
||||
UPDATE fktable SET id = id + 1;
|
||||
|
||||
-- should catch error from initial INSERT
|
||||
COMMIT;
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- doesn't match PK, but no error yet
|
||||
INSERT INTO fktable VALUES (0, 20);
|
||||
|
||||
-- UPDATE will be in a subxact
|
||||
SAVEPOINT savept1;
|
||||
|
||||
-- don't change FK
|
||||
UPDATE fktable SET id = id + 1;
|
||||
|
||||
-- Roll back the UPDATE
|
||||
ROLLBACK TO savept1;
|
||||
|
||||
-- should catch error from initial INSERT
|
||||
COMMIT;
|
||||
|
Loading…
Reference in New Issue
Block a user