skip FK trigger on UPDATE - Mailing list pgsql-patches
From | Neil Conway |
---|---|
Subject | skip FK trigger on UPDATE |
Date | |
Msg-id | 4299C345.6060705@samurai.com Whole thread Raw |
Responses |
Re: skip FK trigger on UPDATE
|
List | pgsql-patches |
This patch implements an idea discussed on -hackers recently: if an UPDATE on a table with a foreign key does not modify any of the table's foreign key columns, we can avoid enqueueing the foreign queue check trigger. I basically just moved the logic for the "are the keys equal?" test from the FK trigger itself into the code that enqueues the trigger. I then removed the keys-are-equal check from the FK trigger. I also had to change (somewhat awkwardly) RI_FKey_keyequal_upd() to work for updates on either the PK table or the FK table. I also removed the bogus TriggerData argument from RI_FKey_keyequal_upd(), since AFAICS it is no needed and merely adds confusion. This patch does cause one change to the regression test output: *** ./expected/foreign_key.out Fri May 27 23:58:54 2005 --- ./results/foreign_key.out Sat May 28 00:46:20 2005 *************** *** 911,918 **** DETAIL: Key (base1,ptest1)=(2,2) is still referenced from table "pktable". -- fails (1,1) is being referenced (twice) update pktable set base1=3 where base1=1; ! ERROR: insert or update on table "pktable" violates foreign key constraint "pktable_base2_fkey" ! DETAIL: Key (base2,ptest2)=(1,1) is not present in table "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; --- 911,918 ---- DETAIL: Key (base1,ptest1)=(2,2) is still referenced from table "pktable". -- fails (1,1) is being referenced (twice) update pktable set base1=3 where base1=1; ! ERROR: update or delete on "pktable" violates foreign key constraint "pktable_base2_fkey" on "pktable" ! DETAIL: Key (base1,ptest1)=(1,1) is still referenced from table "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; I found this a bit strange: on the one hand I think the new error message is actually more sensible, but I'm not sure what caused the change in behavior. I'll have more of a think about this tomorrow. -Neil Index: src/backend/commands/tablecmds.c =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/backend/commands/tablecmds.c,v retrieving revision 1.157 diff -c -r1.157 tablecmds.c *** src/backend/commands/tablecmds.c 10 May 2005 13:16:26 -0000 1.157 --- src/backend/commands/tablecmds.c 27 May 2005 13:58:54 -0000 *************** *** 1596,1603 **** case F_RI_FKEY_NOACTION_UPD: return RI_TRIGGER_PK; ! case F_RI_FKEY_CHECK_INS: ! case F_RI_FKEY_CHECK_UPD: return RI_TRIGGER_FK; } --- 1596,1602 ---- case F_RI_FKEY_NOACTION_UPD: return RI_TRIGGER_PK; ! case F_RI_FKEY_CHECK: return RI_TRIGGER_FK; } *************** *** 4305,4313 **** return; /* ! * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) ! * as if that tuple had just been inserted. If any of those fail, it ! * should ereport(ERROR) and that's that. */ MemSet(&trig, 0, sizeof(trig)); trig.tgoid = InvalidOid; --- 4304,4312 ---- return; /* ! * Scan through each tuple, calling RI_FKey_check (insert trigger) ! * as if that tuple had just been inserted. If any of those fail, ! * it should ereport(ERROR) and that's that. */ MemSet(&trig, 0, sizeof(trig)); trig.tgoid = InvalidOid; *************** *** 4359,4365 **** MemSet(&fcinfo, 0, sizeof(fcinfo)); /* ! * We assume RI_FKey_check_ins won't look at flinfo... */ trigdata.type = T_TriggerData; trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW; --- 4358,4364 ---- MemSet(&fcinfo, 0, sizeof(fcinfo)); /* ! * We assume RI_FKey_check won't look at flinfo... */ trigdata.type = T_TriggerData; trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW; *************** *** 4372,4378 **** fcinfo.context = (Node *) &trigdata; ! RI_FKey_check_ins(&fcinfo); } heap_endscan(scan); --- 4371,4377 ---- fcinfo.context = (Node *) &trigdata; ! RI_FKey_check(&fcinfo); } heap_endscan(scan); *************** *** 4420,4426 **** fk_trigger = makeNode(CreateTrigStmt); fk_trigger->trigname = fkconstraint->constr_name; fk_trigger->relation = myRel; ! fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins"); fk_trigger->before = false; fk_trigger->row = true; fk_trigger->actions[0] = 'i'; --- 4419,4425 ---- fk_trigger = makeNode(CreateTrigStmt); fk_trigger->trigname = fkconstraint->constr_name; fk_trigger->relation = myRel; ! fk_trigger->funcname = SystemFuncName("RI_FKey_check"); fk_trigger->before = false; fk_trigger->row = true; fk_trigger->actions[0] = 'i'; Index: src/backend/commands/trigger.c =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/backend/commands/trigger.c,v retrieving revision 1.188 diff -c -r1.188 trigger.c *** src/backend/commands/trigger.c 6 May 2005 17:24:53 -0000 1.188 --- src/backend/commands/trigger.c 29 May 2005 13:10:25 -0000 *************** *** 111,117 **** bool needconstrrelid = false; void *elem = NULL; ! if (strncmp(strVal(lfirst(list_tail((stmt->funcname)))), "RI_FKey_check_", 14) == 0) { /* A trigger on FK table. */ needconstrrelid = true; --- 111,117 ---- bool needconstrrelid = false; void *elem = NULL; ! if (strncmp(strVal(lfirst(list_tail((stmt->funcname)))), "RI_FKey_check", 13) == 0) { /* A trigger on FK table. */ needconstrrelid = true; *************** *** 2994,3009 **** continue; /* ! * If it is an RI UPDATE trigger, and the referenced keys have ! * not changed, short-circuit queuing of the event; there's no ! * need to fire the trigger. */ if ((event & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_UPDATE) { ! bool is_ri_trigger; switch (trigger->tgfoid) { case F_RI_FKEY_NOACTION_UPD: case F_RI_FKEY_CASCADE_UPD: case F_RI_FKEY_RESTRICT_UPD: --- 2994,3010 ---- continue; /* ! * If this is an UPDATE of a PK table or FK table that does ! * not change the PK or FK respectively, we can skip queuing ! * the event: there is no need to fire the trigger. */ if ((event & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_UPDATE) { ! bool is_ri_trigger = false; switch (trigger->tgfoid) { + case F_RI_FKEY_CHECK: case F_RI_FKEY_NOACTION_UPD: case F_RI_FKEY_CASCADE_UPD: case F_RI_FKEY_RESTRICT_UPD: *************** *** 3011,3043 **** case F_RI_FKEY_SETDEFAULT_UPD: is_ri_trigger = true; break; - - default: - is_ri_trigger = false; - break; } if (is_ri_trigger) { ! TriggerData LocTriggerData; ! ! LocTriggerData.type = T_TriggerData; ! LocTriggerData.tg_event = ! TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_ROW; ! LocTriggerData.tg_relation = rel; ! LocTriggerData.tg_trigtuple = oldtup; ! LocTriggerData.tg_newtuple = newtup; ! LocTriggerData.tg_trigger = trigger; ! /* ! * We do not currently know which buffers the passed tuples ! * are in, but it does not matter because RI_FKey_keyequal_upd ! * does not care. We could expand the API of this function ! * if it becomes necessary to set these fields accurately. ! */ ! LocTriggerData.tg_trigtuplebuf = InvalidBuffer; ! LocTriggerData.tg_newtuplebuf = InvalidBuffer; ! ! if (RI_FKey_keyequal_upd(&LocTriggerData)) { /* key unchanged, so skip queuing this event */ continue; --- 3012,3035 ---- case F_RI_FKEY_SETDEFAULT_UPD: is_ri_trigger = true; break; } + /* + * There is one exception when updating FK tables: if the + * updated row was inserted by our own transaction and the + * FK is deferred, we still need to fire the trigger. This + * is because our UPDATE will invalidate the INSERT so the + * end-of-transaction INSERT RI trigger will not do + * anything, so we have to do the check for the UPDATE + * anyway. + */ + if (trigger->tgfoid == F_RI_FKEY_CHECK && + HeapTupleHeaderGetXmin(oldtup->t_data) == GetCurrentTransactionId()) + is_ri_trigger = false; + if (is_ri_trigger) { ! if (RI_FKey_keyequal_upd(trigger, rel, oldtup, newtup)) { /* key unchanged, so skip queuing this event */ continue; Index: src/backend/utils/adt/ri_triggers.c =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/backend/utils/adt/ri_triggers.c,v retrieving revision 1.78 diff -c -r1.78 ri_triggers.c *** src/backend/utils/adt/ri_triggers.c 29 May 2005 04:23:05 -0000 1.78 --- src/backend/utils/adt/ri_triggers.c 29 May 2005 13:22:09 -0000 *************** *** 38,47 **** #include "optimizer/planmain.h" #include "parser/parse_oper.h" #include "rewrite/rewriteHandler.h" - #include "utils/lsyscache.h" - #include "utils/typcache.h" #include "utils/acl.h" #include "utils/guc.h" #include "miscadmin.h" --- 38,48 ---- #include "optimizer/planmain.h" #include "parser/parse_oper.h" #include "rewrite/rewriteHandler.h" #include "utils/acl.h" + #include "utils/fmgroids.h" #include "utils/guc.h" + #include "utils/lsyscache.h" + #include "utils/typcache.h" #include "miscadmin.h" *************** *** 176,182 **** * Check foreign key existence (combined for INSERT and UPDATE). * ---------- */ ! static Datum RI_FKey_check(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; --- 177,183 ---- * Check foreign key existence (combined for INSERT and UPDATE). * ---------- */ ! Datum RI_FKey_check(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; *************** *** 375,396 **** break; } - /* - * No need to check anything if old and new references are the same on - * UPDATE. - */ - if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) - { - if (HeapTupleHeaderGetXmin(old_row->t_data) != - GetCurrentTransactionId() && - 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"); --- 376,381 ---- *************** *** 455,486 **** /* ---------- - * RI_FKey_check_ins - - * - * Check foreign key existence at insert event on FK table. - * ---------- - */ - Datum - RI_FKey_check_ins(PG_FUNCTION_ARGS) - { - return RI_FKey_check(fcinfo); - } - - - /* ---------- - * RI_FKey_check_upd - - * - * Check foreign key existence at update event on FK table. - * ---------- - */ - Datum - RI_FKey_check_upd(PG_FUNCTION_ARGS) - { - return RI_FKey_check(fcinfo); - } - - - /* ---------- * ri_Check_Pk_Match * * Check for matching value of old pk row in current state for --- 440,445 ---- *************** *** 2005,2012 **** * corresponding to changed columns in pk_rel's key */ if (match_type == RI_MATCH_TYPE_FULL || ! !ri_OneKeyEqual(pk_rel, i, old_row, new_row, &qkey, ! RI_KEYPAIR_PK_IDX)) { snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), "%s %s = NULL", querysep, attname); --- 1964,1971 ---- * corresponding to changed columns in pk_rel's key */ if (match_type == RI_MATCH_TYPE_FULL || ! !ri_OneKeyEqual(pk_rel, i, old_row, new_row, &qkey, ! RI_KEYPAIR_PK_IDX)) { snprintf(querystr + strlen(querystr), sizeof(querystr) - strlen(querystr), "%s %s = NULL", querysep, attname); *************** *** 2016,2022 **** qualsep, attname, i + 1); qualsep = "AND"; queryoids[i] = SPI_gettypeid(pk_rel->rd_att, ! qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } strcat(querystr, qualstr); --- 1975,1981 ---- qualsep, attname, i + 1); qualsep = "AND"; queryoids[i] = SPI_gettypeid(pk_rel->rd_att, ! qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } strcat(querystr, qualstr); *************** *** 2453,2480 **** /* ---------- * RI_FKey_keyequal_upd - * ! * Check if we have a key change on update. ! * ! * This is not a real trigger procedure. It is used by the AFTER * trigger queue manager to detect "triggered data change violation". * ---------- */ bool ! RI_FKey_keyequal_upd(TriggerData *trigdata) { int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; - HeapTuple new_row; - HeapTuple old_row; RI_QueryKey qkey; /* * Check for the correct # of call arguments */ ! tgnargs = trigdata->tg_trigger->tgnargs; ! tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || tgnargs > RI_MAX_ARGUMENTS || (tgnargs % 2) != 0) --- 2412,2437 ---- /* ---------- * RI_FKey_keyequal_upd - * ! * Check if we have a key change on update. It is used by the AFTER * trigger queue manager to detect "triggered data change violation". * ---------- */ bool ! RI_FKey_keyequal_upd(Trigger *trigger, Relation rel, HeapTuple old_row, HeapTuple new_row) { int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; RI_QueryKey qkey; + bool fk_update; + bool result; /* * Check for the correct # of call arguments */ ! tgnargs = trigger->tgnargs; ! tgargs = trigger->tgargs; if (tgnargs < 4 || tgnargs > RI_MAX_ARGUMENTS || (tgnargs % 2) != 0) *************** *** 2490,2548 **** return true; /* ! * Get the relation descriptors of the FK and PK tables and the new ! * and old tuple. ! * ! * Use minimal locking for fk_rel here. */ ! if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), ! errmsg("no target table given for trigger \"%s\" on table \"%s\"", ! trigdata->tg_trigger->tgname, ! RelationGetRelationName(trigdata->tg_relation)), ! errhint("Remove this referential integrity trigger and its mates, then do ALTER TABLE ADD CONSTRAINT."))); ! ! fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, AccessShareLock); ! pk_rel = trigdata->tg_relation; ! new_row = trigdata->tg_newtuple; ! old_row = trigdata->tg_trigtuple; ! ! switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO])) { ! /* ! * MATCH <UNSPECIFIED> ! */ ! case RI_MATCH_TYPE_UNSPECIFIED: ! case RI_MATCH_TYPE_FULL: ! ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid, ! RI_PLAN_KEYEQUAL_UPD, ! fk_rel, pk_rel, ! tgnargs, tgargs); ! ! heap_close(fk_rel, AccessShareLock); ! /* ! * Return if key's are equal ! */ ! return ri_KeysEqual(pk_rel, old_row, new_row, &qkey, ! RI_KEYPAIR_PK_IDX); ! /* ! * Handle MATCH PARTIAL set null delete. ! */ ! case RI_MATCH_TYPE_PARTIAL: ! ereport(ERROR, ! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("MATCH PARTIAL not yet implemented"))); ! break; ! } /* ! * Never reached */ ! elog(ERROR, "invalid match_type"); ! return false; } --- 2447,2499 ---- return true; /* ! * This can be invoked for both UPDATEs to a PK table or an FK ! * table. "rel" always refers to the relation on which the UPDATE ! * occurred. */ ! if (!OidIsValid(trigger->tgconstrrelid)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), ! errmsg("no target table given for trigger \"%s\" on table \"%s\"", ! trigger->tgname, ! RelationGetRelationName(rel)), ! errhint("Remove this referential integrity trigger and its mates, " ! "then do ALTER TABLE ADD CONSTRAINT."))); ! ! if (trigger->tgfoid == F_RI_FKEY_CHECK) ! { ! /* Update on FK table */ ! fk_update = true; ! fk_rel = rel; ! pk_rel = heap_open(trigger->tgconstrrelid, AccessShareLock); ! } ! else { ! /* Update on PK table */ ! fk_update = false; ! pk_rel = rel; ! fk_rel = heap_open(trigger->tgconstrrelid, AccessShareLock); ! } ! if (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]) == RI_MATCH_TYPE_PARTIAL) ! ereport(ERROR, ! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("MATCH PARTIAL not yet implemented"))); ! /* MATCH <UNSPECIFIED> and MATCH <FULL> */ ! ri_BuildQueryKeyFull(&qkey, trigger->tgoid, RI_PLAN_KEYEQUAL_UPD, ! fk_rel, pk_rel, tgnargs, tgargs); /* ! * Return if keys are equal */ ! result = ri_KeysEqual(rel, old_row, new_row, &qkey, ! fk_update ? RI_KEYPAIR_FK_IDX : RI_KEYPAIR_PK_IDX); ! if (fk_update) ! heap_close(pk_rel, AccessShareLock); ! else ! heap_close(fk_rel, AccessShareLock); ! return result; } *************** *** 2871,2877 **** /* * Initialize the key and fill in type, oid's and number of keypairs */ ! memset((void *) key, 0, sizeof(RI_QueryKey)); key->constr_type = RI_MATCH_TYPE_FULL; key->constr_id = constr_id; key->constr_queryno = constr_queryno; --- 2822,2828 ---- /* * Initialize the key and fill in type, oid's and number of keypairs */ ! memset(key, 0, sizeof(RI_QueryKey)); key->constr_type = RI_MATCH_TYPE_FULL; key->constr_id = constr_id; key->constr_queryno = constr_queryno; *************** *** 3489,3495 **** for (i = 0; i < key->nkeypairs; i++) { /* ! * Get one attributes oldvalue. If it is NULL - they're not equal. */ oldvalue = SPI_getbinval(oldtup, rel->rd_att, key->keypair[i][pairidx], &isnull); --- 3440,3446 ---- for (i = 0; i < key->nkeypairs; i++) { /* ! * Get one attribute's oldvalue. If it is NULL - they're not equal. */ oldvalue = SPI_getbinval(oldtup, rel->rd_att, key->keypair[i][pairidx], &isnull); *************** *** 3497,3503 **** return false; /* ! * Get one attributes oldvalue. If it is NULL - they're not equal. */ newvalue = SPI_getbinval(newtup, rel->rd_att, key->keypair[i][pairidx], &isnull); --- 3448,3454 ---- return false; /* ! * Get one attribute's oldvalue. If it is NULL - they're not equal. */ newvalue = SPI_getbinval(newtup, rel->rd_att, key->keypair[i][pairidx], &isnull); *************** *** 3505,3511 **** return false; /* ! * Get the attributes type OID and call the '=' operator to * compare the values. */ typeid = SPI_gettypeid(rel->rd_att, key->keypair[i][pairidx]); --- 3456,3462 ---- return false; /* ! * Get the attribute's type OID and call the '=' operator to * compare the values. */ typeid = SPI_gettypeid(rel->rd_att, key->keypair[i][pairidx]); Index: src/include/catalog/pg_proc.h =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/include/catalog/pg_proc.h,v retrieving revision 1.363 diff -c -r1.363 pg_proc.h *** src/include/catalog/pg_proc.h 20 May 2005 01:29:55 -0000 1.363 --- src/include/catalog/pg_proc.h 27 May 2005 13:58:54 -0000 *************** *** 2261,2269 **** /* Generic referential integrity constraint triggers */ ! DATA(insert OID = 1644 ( RI_FKey_check_ins PGNSP PGUID 12 f f t f v 0 2279 "" _null_ _null_ _null_ RI_FKey_check_ins- _null_ )); ! DESCR("referential integrity FOREIGN KEY ... REFERENCES"); ! DATA(insert OID = 1645 ( RI_FKey_check_upd PGNSP PGUID 12 f f t f v 0 2279 "" _null_ _null_ _null_ RI_FKey_check_upd- _null_ )); DESCR("referential integrity FOREIGN KEY ... REFERENCES"); DATA(insert OID = 1646 ( RI_FKey_cascade_del PGNSP PGUID 12 f f t f v 0 2279 "" _null_ _null_ _null_ RI_FKey_cascade_del- _null_ )); DESCR("referential integrity ON DELETE CASCADE"); --- 2261,2267 ---- /* Generic referential integrity constraint triggers */ ! DATA(insert OID = 1644 ( RI_FKey_check PGNSP PGUID 12 f f t f v 0 2279 "" _null_ _null_ _null_ RI_FKey_check- _null_ )); DESCR("referential integrity FOREIGN KEY ... REFERENCES"); DATA(insert OID = 1646 ( RI_FKey_cascade_del PGNSP PGUID 12 f f t f v 0 2279 "" _null_ _null_ _null_ RI_FKey_cascade_del- _null_ )); DESCR("referential integrity ON DELETE CASCADE"); Index: src/include/commands/trigger.h =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/include/commands/trigger.h,v retrieving revision 1.53 diff -c -r1.53 trigger.h *** src/include/commands/trigger.h 11 Apr 2005 19:51:15 -0000 1.53 --- src/include/commands/trigger.h 27 May 2005 13:58:54 -0000 *************** *** 168,174 **** /* * in utils/adt/ri_triggers.c */ ! extern bool RI_FKey_keyequal_upd(TriggerData *trigdata); extern bool RI_Initial_Check(FkConstraint *fkconstraint, Relation rel, Relation pkrel); --- 168,175 ---- /* * in utils/adt/ri_triggers.c */ ! extern bool RI_FKey_keyequal_upd(Trigger *trigger, Relation rel, ! HeapTuple old_row, HeapTuple new_row); extern bool RI_Initial_Check(FkConstraint *fkconstraint, Relation rel, Relation pkrel); Index: src/include/utils/builtins.h =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/include/utils/builtins.h,v retrieving revision 1.257 diff -c -r1.257 builtins.h *** src/include/utils/builtins.h 27 May 2005 00:57:49 -0000 1.257 --- src/include/utils/builtins.h 27 May 2005 13:58:54 -0000 *************** *** 782,789 **** extern Datum width_bucket_numeric(PG_FUNCTION_ARGS); /* ri_triggers.c */ ! extern Datum RI_FKey_check_ins(PG_FUNCTION_ARGS); ! extern Datum RI_FKey_check_upd(PG_FUNCTION_ARGS); extern Datum RI_FKey_noaction_del(PG_FUNCTION_ARGS); extern Datum RI_FKey_noaction_upd(PG_FUNCTION_ARGS); extern Datum RI_FKey_cascade_del(PG_FUNCTION_ARGS); --- 782,788 ---- extern Datum width_bucket_numeric(PG_FUNCTION_ARGS); /* ri_triggers.c */ ! extern Datum RI_FKey_check(PG_FUNCTION_ARGS); extern Datum RI_FKey_noaction_del(PG_FUNCTION_ARGS); extern Datum RI_FKey_noaction_upd(PG_FUNCTION_ARGS); extern Datum RI_FKey_cascade_del(PG_FUNCTION_ARGS); Index: src/test/regress/expected/foreign_key.out =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/test/regress/expected/foreign_key.out,v retrieving revision 1.38 diff -c -r1.38 foreign_key.out *** src/test/regress/expected/foreign_key.out 13 Oct 2004 01:22:31 -0000 1.38 --- src/test/regress/expected/foreign_key.out 27 May 2005 13:58:54 -0000 *************** *** 911,918 **** DETAIL: Key (base1,ptest1)=(2,2) is still referenced from table "pktable". -- fails (1,1) is being referenced (twice) update pktable set base1=3 where base1=1; ! ERROR: update or delete on "pktable" violates foreign key constraint "pktable_base2_fkey" on "pktable" ! DETAIL: Key (base1,ptest1)=(1,1) is still referenced from table "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; --- 911,918 ---- DETAIL: Key (base1,ptest1)=(2,2) is still referenced from table "pktable". -- fails (1,1) is being referenced (twice) update pktable set base1=3 where base1=1; ! ERROR: insert or update on table "pktable" violates foreign key constraint "pktable_base2_fkey" ! DETAIL: Key (base2,ptest2)=(1,1) is not present in table "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; *************** *** 1061,1066 **** --- 1061,1068 ---- COMMIT; ERROR: insert or update on table "fktable" violates foreign key constraint "fktable_fk_fkey" DETAIL: Key (fk)=(200) is not present in table "pktable". + DROP TABLE pktable, fktable CASCADE; + NOTICE: drop cascades to constraint fktable_fk_fkey on table fktable -- test notice about expensive referential integrity checks, -- where the index cannot be used because of type incompatibilities. CREATE TEMP TABLE pktable ( *************** *** 1128,1130 **** --- 1130,1174 ---- DETAIL: Key columns "x2" and "id1" are of different types: character varying and integer. WARNING: foreign key constraint "fk_241_132" will require costly sequential scans DETAIL: Key columns "x4" and "id3" are of different types: text and real. + DROP TABLE pktable, fktable CASCADE; + NOTICE: drop cascades to constraint fk_241_132 on table fktable + NOTICE: drop cascades to constraint fk_123_231 on table fktable + NOTICE: drop cascades to constraint fk_253_213 on table fktable + NOTICE: drop cascades to constraint fk_213_213 on table fktable + NOTICE: drop cascades to constraint fk_123_123 on table fktable + NOTICE: drop cascades to constraint fk_5_1 on table fktable + NOTICE: drop cascades to constraint fk_3_1 on table fktable + NOTICE: drop cascades to constraint fk_2_1 on table fktable + NOTICE: drop cascades to constraint fktable_x1_fkey on table fktable + NOTICE: drop cascades to constraint fk_4_2 on table fktable + NOTICE: drop cascades to constraint fk_1_2 on table fktable + NOTICE: drop cascades to constraint fktable_x2_fkey on table fktable + NOTICE: drop cascades to constraint fk_1_3 on table fktable + NOTICE: drop cascades to constraint fk_2_3 on table fktable + NOTICE: drop cascades to constraint fktable_x3_fkey on table fktable + -- test a tricky case: we can elide firing the FK check trigger during + -- an UPDATE if the UPDATE did not change the foreign key + -- field. However, we can't do this if our transaction was the one that + -- created the updated row and the trigger is deferred, since our UPDATE + -- will have invalidated the original newly-inserted tuple, and therefore + -- cause the on-INSERT RI trigger not to be fired. + CREATE TEMP TABLE pktable ( + id int primary key, + other int + ); + NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable" + CREATE TEMP TABLE fktable ( + id int primary key, + fk int references pktable deferrable initially deferred + ); + NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "fktable_pkey" for table "fktable" + INSERT INTO pktable VALUES (5, 10); + BEGIN; + -- doesn't match PK, but no error yet + INSERT INTO fktable VALUES (0, 20); + -- 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". Index: src/test/regress/sql/foreign_key.sql =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/test/regress/sql/foreign_key.sql,v retrieving revision 1.15 diff -c -r1.15 foreign_key.sql *** src/test/regress/sql/foreign_key.sql 4 Aug 2004 21:34:34 -0000 1.15 --- src/test/regress/sql/foreign_key.sql 27 May 2005 13:58:54 -0000 *************** *** 705,710 **** --- 705,712 ---- -- error here on commit COMMIT; + DROP TABLE pktable, fktable CASCADE; + -- test notice about expensive referential integrity checks, -- where the index cannot be used because of type incompatibilities. *************** *** 774,776 **** --- 776,810 ---- ALTER TABLE fktable ADD CONSTRAINT fk_241_132 FOREIGN KEY (x2,x4,x1) REFERENCES pktable(id1,id3,id2); + + DROP TABLE pktable, fktable CASCADE; + + -- test a tricky case: we can elide firing the FK check trigger during + -- an UPDATE if the UPDATE did not change the foreign key + -- field. However, we can't do this if our transaction was the one that + -- created the updated row and the trigger is deferred, since our UPDATE + -- will have invalidated the original newly-inserted tuple, and therefore + -- cause the on-INSERT RI trigger not to be fired. + + CREATE TEMP TABLE pktable ( + id int primary key, + other int + ); + + CREATE TEMP TABLE fktable ( + id int primary key, + fk int references pktable deferrable initially deferred + ); + + INSERT INTO pktable VALUES (5, 10); + + BEGIN; + + -- doesn't match PK, but no error yet + INSERT INTO fktable VALUES (0, 20); + + -- don't change FK + UPDATE fktable SET id = id + 1; + + -- should catch error from initial INSERT + COMMIT;
pgsql-patches by date: