mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-24 18:55:04 +08:00
Avoid primary key lookup (and lock) if foreign key does not change
on UPDATE. This get's rid of the long standing annoyance that updating a row that has foreign keys locks all the referenced rows even if the foreign key values do not change. The trick is to actually do a check identical to NO ACTION after an eventually done UPDATE in the SET DEFAULT case. Since a SET DEFAULT operation should have moved referencing rows to a new "home", a following NO ACTION check can only fail if the column defaults of the referencing table resulted in the key we actually deleted. Thanks to Stephan. Jan
This commit is contained in:
parent
afe1185cf0
commit
cd203f3395
@ -17,7 +17,7 @@
|
||||
*
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.48 2003/03/27 19:25:40 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.49 2003/04/07 20:30:38 wieck Exp $
|
||||
*
|
||||
* ----------
|
||||
*/
|
||||
@ -395,13 +395,19 @@ RI_FKey_check(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: We cannot avoid the check on UPDATE, even if old and new key
|
||||
* are the same. Otherwise, someone could DELETE the PK that consists
|
||||
* of the DEFAULT values, and if there are any references, a ON DELETE
|
||||
* SET DEFAULT action would update the references to exactly these
|
||||
* values but we wouldn't see that weird case (this is the only place
|
||||
* to see it).
|
||||
* No need to check anything if old and new references are the
|
||||
* same on UPDATE.
|
||||
*/
|
||||
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
|
||||
{
|
||||
if (ri_KeysEqual(fk_rel, old_row, new_row, &qkey,
|
||||
RI_KEYPAIR_FK_IDX))
|
||||
{
|
||||
heap_close(pk_rel, RowShareLock);
|
||||
return PointerGetDatum(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (SPI_connect() != SPI_OK_CONNECT)
|
||||
elog(ERROR, "SPI_connect() failed in RI_FKey_check()");
|
||||
|
||||
@ -2397,6 +2403,16 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
|
||||
|
||||
heap_close(fk_rel, RowExclusiveLock);
|
||||
|
||||
/*
|
||||
* In the case we delete the row who's key is equal to the
|
||||
* default values AND a referencing row in the foreign key
|
||||
* table exists, we would just have updated it to the same
|
||||
* values. We need to do another lookup now and in case a
|
||||
* reference exists, abort the operation. That is already
|
||||
* implemented in the NO ACTION trigger.
|
||||
*/
|
||||
RI_FKey_noaction_del(fcinfo);
|
||||
|
||||
return PointerGetDatum(NULL);
|
||||
|
||||
/*
|
||||
@ -2635,6 +2651,16 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
|
||||
|
||||
heap_close(fk_rel, RowExclusiveLock);
|
||||
|
||||
/*
|
||||
* In the case we updated the row who's key was equal to the
|
||||
* default values AND a referencing row in the foreign key
|
||||
* table exists, we would just have updated it to the same
|
||||
* values. We need to do another lookup now and in case a
|
||||
* reference exists, abort the operation. That is already
|
||||
* implemented in the NO ACTION trigger.
|
||||
*/
|
||||
RI_FKey_noaction_upd(fcinfo);
|
||||
|
||||
return PointerGetDatum(NULL);
|
||||
|
||||
/*
|
||||
|
@ -882,7 +882,7 @@ delete from pktable where base1=2;
|
||||
ERROR: $1 referential integrity violation - key (base1,ptest1)=(2,2) in pktable still referenced from pktable
|
||||
-- fails (1,1) is being referenced (twice)
|
||||
update pktable set base1=3 where base1=1;
|
||||
ERROR: $1 referential integrity violation - key (base2,ptest2)=(1,1) referenced from pktable not found in pktable
|
||||
ERROR: $1 referential integrity violation - key (base1,ptest1)=(1,1) in pktable still referenced from pktable
|
||||
-- this sequence of two deletes will work, since after the first there will be no (2,*) references
|
||||
delete from pktable where base2=2;
|
||||
delete from pktable where base1=2;
|
||||
|
Loading…
Reference in New Issue
Block a user