]> granicus.if.org Git - postgresql/commitdiff
Back-patch Jan's fix to avoid primary key lookup (and lock) if foreign key
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 21 May 2003 18:14:46 +0000 (18:14 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 21 May 2003 18:14:46 +0000 (18:14 +0000)
does not change on UPDATE.

src/backend/utils/adt/ri_triggers.c
src/test/regress/expected/foreign_key.out

index 50b2d28d875afafd25541b47387bbd8eb23b2ce3..539dee726b28be2bf3104c48df41bbf37c8b44c9 100644 (file)
@@ -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.43.2.2 2003/04/26 22:21:58 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.43.2.3 2003/05/21 18:14:46 tgl 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 weired 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(WARNING, "SPI_connect() failed in RI_FKey_check()");
 
@@ -2741,6 +2747,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);
 
                        /*
@@ -3010,6 +3026,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);
 
                        /*
index a68a406fdacd78678a22bb87cade5f4beca35c6c..3c2ede0aaa39de26a0bd1818dff44629492a7c28 100644 (file)
@@ -882,7 +882,7 @@ delete from pktable where base1=2;
 ERROR:  $1 referential integrity violation - key 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 referenced from pktable not found in pktable
+ERROR:  $1 referential integrity violation - key 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;