From b7b6d4bf532512bea7ceb46b6250f9f617948ef4 Mon Sep 17 00:00:00 2001 From: Jan Wieck Date: Thu, 6 Jan 2000 20:47:01 +0000 Subject: [PATCH] Changed "triggered data change violation" detection code in trigger manager. Jan --- src/backend/commands/trigger.c | 326 ++++++++++++++-------------- src/backend/parser/analyze.c | 250 ++++++++++----------- src/backend/utils/adt/ri_triggers.c | 125 ++++++++++- src/include/catalog/pg_proc.h | 6 +- src/include/commands/trigger.h | 10 +- src/include/utils/builtins.h | 4 +- 6 files changed, 427 insertions(+), 294 deletions(-) diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index daa5b8e717..b0196c90de 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -1054,20 +1054,6 @@ deferredTriggerGetPreviousEvent(Oid relid, ItemPointer ctid) } -/* ---------- - * deferredTriggerCancelEvent() - * - * Mark an event in the eventlist as cancelled because it isn't - * required anymore (replaced by anoter event). - * ---------- - */ -static void -deferredTriggerCancelEvent(DeferredTriggerEvent event) -{ - event->dte_event |= TRIGGER_DEFERRED_CANCELED; -} - - /* ---------- * deferredTriggerExecute() * @@ -1112,10 +1098,11 @@ deferredTriggerExecute(DeferredTriggerEvent event, int itemno) * Setup the trigger information * ---------- */ - SaveTriggerData.tg_event = event->dte_event | TRIGGER_EVENT_ROW; + SaveTriggerData.tg_event = (event->dte_event & TRIGGER_EVENT_OPMASK) | + TRIGGER_EVENT_ROW; SaveTriggerData.tg_relation = rel; - switch (event->dte_event) + switch (event->dte_event & TRIGGER_EVENT_OPMASK) { case TRIGGER_EVENT_INSERT: SaveTriggerData.tg_trigtuple = &newtuple; @@ -1656,13 +1643,13 @@ DeferredTriggerSaveEvent(Relation rel, int event, MemoryContext oldcxt; DeferredTriggerEvent new_event; DeferredTriggerEvent prev_event; - bool prev_done = false; int new_size; int i; int ntriggers; Trigger **triggers; ItemPointerData oldctid; ItemPointerData newctid; + TriggerData SaveTriggerData; if (deftrig_cxt == NULL) elog(ERROR, @@ -1694,203 +1681,212 @@ DeferredTriggerSaveEvent(Relation rel, int event, ItemPointerSetInvalid(&(newctid)); /* ---------- - * Eventually modify the event and do some general RI violation checks + * Create a new event * ---------- */ - switch (event) + oldcxt = MemoryContextSwitchTo((MemoryContext) deftrig_cxt); + + ntriggers = rel->trigdesc->n_after_row[event]; + triggers = rel->trigdesc->tg_after_row[event]; + + new_size = sizeof(DeferredTriggerEventData) + + ntriggers * sizeof(DeferredTriggerEventItem); + + new_event = (DeferredTriggerEvent) palloc(new_size); + new_event->dte_event = event & TRIGGER_EVENT_OPMASK; + new_event->dte_relid = rel->rd_id; + ItemPointerCopy(&oldctid, &(new_event->dte_oldctid)); + ItemPointerCopy(&newctid, &(new_event->dte_newctid)); + new_event->dte_n_items = ntriggers; + new_event->dte_item[ntriggers].dti_state = new_size; + for (i = 0; i < ntriggers; i++) + { + new_event->dte_item[i].dti_tgoid = triggers[i]->tgoid; + new_event->dte_item[i].dti_state = + ((triggers[i]->tgdeferrable) ? + TRIGGER_DEFERRED_DEFERRABLE : 0) | + ((triggers[i]->tginitdeferred) ? + TRIGGER_DEFERRED_INITDEFERRED : 0) | + ((rel->trigdesc->n_before_row[event] > 0) ? + TRIGGER_DEFERRED_HAS_BEFORE : 0); + } + MemoryContextSwitchTo(oldcxt); + + switch (event & TRIGGER_EVENT_OPMASK) { case TRIGGER_EVENT_INSERT: - /* ---------- - * Don't know how to (surely) check if another tuple with - * this meaning (from all FK's point of view) got deleted - * in the same transaction. Thus not handled yet. - * ---------- - */ + new_event->dte_event |= TRIGGER_DEFERRED_ROW_INSERTED; + new_event->dte_event |= TRIGGER_DEFERRED_KEY_CHANGED; break; case TRIGGER_EVENT_UPDATE: /* ---------- - * On UPDATE check if the tuple updated is a result - * of the same transaction. + * On UPDATE check if the tuple updated has been inserted + * or a foreign referenced key value that's changing now + * has been updated once before in this transaction. * ---------- */ if (oldtup->t_data->t_xmin != GetCurrentTransactionId()) - break; + prev_event = NULL; + else + prev_event = + deferredTriggerGetPreviousEvent(rel->rd_id, &oldctid); /* ---------- - * Look at the previous event to the same tuple if - * any of its triggers has already been executed. - * Such a situation would potentially violate RI - * so we abort the transaction. + * Now check if one of the referenced keys is changed. * ---------- */ - prev_event = deferredTriggerGetPreviousEvent(rel->rd_id, &oldctid); - if (prev_event->dte_event & TRIGGER_DEFERRED_HAS_BEFORE || - (prev_event->dte_n_items != 0 && - prev_event->dte_event & TRIGGER_DEFERRED_DONE)) - prev_done = true; - else - for (i = 0; i < prev_event->dte_n_items; i++) + for (i = 0; i < ntriggers; i++) + { + bool is_ri_trigger; + bool key_unchanged; + + /* ---------- + * We are interested in RI_FKEY triggers only. + * ---------- + */ + switch (triggers[i]->tgfoid) { - if (prev_event->dte_item[i].dti_state & - TRIGGER_DEFERRED_DONE) - { - prev_done = true; + case F_RI_FKEY_NOACTION_UPD: + is_ri_trigger = true; + new_event->dte_item[i].dti_state |= + TRIGGER_DEFERRED_DONE; break; - } - } - if (prev_done) - { - elog(NOTICE, "UPDATE of row inserted/updated in same " - "transaction violates"); - elog(NOTICE, "referential integrity semantics. Other " - "triggers or IMMEDIATE "); - elog(ERROR, " constraints have already been executed."); - } + case F_RI_FKEY_CASCADE_UPD: + case F_RI_FKEY_RESTRICT_UPD: + case F_RI_FKEY_SETNULL_UPD: + case F_RI_FKEY_SETDEFAULT_UPD: + is_ri_trigger = true; + break; + + default: + is_ri_trigger = false; + break; + } + if (!is_ri_trigger) + continue; + + SaveTriggerData.tg_event = TRIGGER_EVENT_UPDATE; + SaveTriggerData.tg_relation = rel; + SaveTriggerData.tg_trigtuple = oldtup; + SaveTriggerData.tg_newtuple = newtup; + SaveTriggerData.tg_trigger = triggers[i]; + + CurrentTriggerData = &SaveTriggerData; + key_unchanged = RI_FKey_keyequal_upd(); + CurrentTriggerData = NULL; + + if (key_unchanged) + { + /* ---------- + * The key hasn't changed, so no need later to invoke + * the trigger at all. But remember other states from + * the possible earlier event. + * ---------- + */ + new_event->dte_item[i].dti_state |= TRIGGER_DEFERRED_DONE; - /* ---------- - * Anything's fine so far - i.e. none of the previous - * events triggers has been executed up to now. Let's - * the REAL event that happened so far. - * ---------- - */ - switch (prev_event->dte_event & TRIGGER_EVENT_OPMASK) - { - case TRIGGER_EVENT_INSERT: + if (prev_event) + { + if (prev_event->dte_event & + TRIGGER_DEFERRED_ROW_INSERTED) + { + /* ---------- + * This is a row inserted during our transaction. + * So any key value is considered changed. + * ---------- + */ + new_event->dte_event |= + TRIGGER_DEFERRED_ROW_INSERTED; + new_event->dte_event |= + TRIGGER_DEFERRED_KEY_CHANGED; + new_event->dte_item[i].dti_state |= + TRIGGER_DEFERRED_KEY_CHANGED; + } + else + { + /* ---------- + * This is a row, previously updated. So + * if this key has been changed before, we + * still remember that it happened. + * ---------- + */ + if (prev_event->dte_item[i].dti_state & + TRIGGER_DEFERRED_KEY_CHANGED) + { + new_event->dte_item[i].dti_state |= + TRIGGER_DEFERRED_KEY_CHANGED; + new_event->dte_event |= + TRIGGER_DEFERRED_KEY_CHANGED; + } + } + } + } + else + { /* ---------- - * The previous operation was an insert. - * So the REAL new event is an INSERT of - * the new tuple. + * Bomb out if this key has been changed before. + * Otherwise remember that we do so. * ---------- */ - event = TRIGGER_EVENT_INSERT; - ItemPointerSetInvalid(&oldctid); - deferredTriggerCancelEvent(prev_event); - break; + if (prev_event) + { + if (prev_event->dte_event & + TRIGGER_DEFERRED_ROW_INSERTED) + elog(ERROR, "triggered data change violation " + "on relation \"%s\"", + nameout(&(rel->rd_rel->relname))); + + if (prev_event->dte_item[i].dti_state & + TRIGGER_DEFERRED_KEY_CHANGED) + elog(ERROR, "triggered data change violation " + "on relation \"%s\"", + nameout(&(rel->rd_rel->relname))); + } - case TRIGGER_EVENT_UPDATE: /* ---------- - * The previous operation was an UPDATE. - * So the REAL new event is still an UPDATE - * but from the original tuple to this new one. + * This is the first change to this key, so let + * it happen. * ---------- */ - event = TRIGGER_EVENT_UPDATE; - ItemPointerCopy(&(prev_event->dte_oldctid), &oldctid); - deferredTriggerCancelEvent(prev_event); - break; + new_event->dte_item[i].dti_state |= + TRIGGER_DEFERRED_KEY_CHANGED; + new_event->dte_event |= TRIGGER_DEFERRED_KEY_CHANGED; + } } break; case TRIGGER_EVENT_DELETE: /* ---------- - * On DELETE check if the tuple updated is a result - * of the same transaction. + * On DELETE check if the tuple deleted has been inserted + * or a possibly referenced key value has changed in this + * transaction. * ---------- */ if (oldtup->t_data->t_xmin != GetCurrentTransactionId()) break; /* ---------- - * Look at the previous event to the same tuple if - * any of its triggers has already been executed. - * Such a situation would potentially violate RI - * so we abort the transaction. + * Look at the previous event to the same tuple. * ---------- */ prev_event = deferredTriggerGetPreviousEvent(rel->rd_id, &oldctid); - if (prev_event->dte_event & TRIGGER_DEFERRED_HAS_BEFORE || - (prev_event->dte_n_items != 0 && - prev_event->dte_event & TRIGGER_DEFERRED_DONE)) - prev_done = true; - else - for (i = 0; i < prev_event->dte_n_items; i++) - { - if (prev_event->dte_item[i].dti_state & - TRIGGER_DEFERRED_DONE) - { - prev_done = true; - break; - } - } - - if (prev_done) - { - elog(NOTICE, "DELETE of row inserted/updated in same " - "transaction violates"); - elog(NOTICE, "referential integrity semantics. Other " - "triggers or IMMEDIATE "); - elog(ERROR, " constraints have already been executed."); - } - - /* ---------- - * Anything's fine so far - i.e. none of the previous - * events triggers has been executed up to now. Let's - * the REAL event that happened so far. - * ---------- - */ - switch (prev_event->dte_event & TRIGGER_EVENT_OPMASK) - { - case TRIGGER_EVENT_INSERT: - /* ---------- - * The previous operation was an insert. - * So the REAL new event is nothing. - * ---------- - */ - deferredTriggerCancelEvent(prev_event); - return; - - case TRIGGER_EVENT_UPDATE: - /* ---------- - * The previous operation was an UPDATE. - * So the REAL new event is a DELETE - * but from the original tuple. - * ---------- - */ - event = TRIGGER_EVENT_DELETE; - ItemPointerCopy(&(prev_event->dte_oldctid), &oldctid); - deferredTriggerCancelEvent(prev_event); - break; - } + if (prev_event->dte_event & TRIGGER_DEFERRED_KEY_CHANGED) + elog(ERROR, "triggered data change violation " + "on relation \"%s\"", + nameout(&(rel->rd_rel->relname))); break; } - + /* ---------- - * Create a new event and save it. + * Anything's fine up to here. Add the new event to the queue. * ---------- */ oldcxt = MemoryContextSwitchTo((MemoryContext) deftrig_cxt); - - ntriggers = rel->trigdesc->n_after_row[event]; - triggers = rel->trigdesc->tg_after_row[event]; - - new_size = sizeof(DeferredTriggerEventData) + - ntriggers * sizeof(DeferredTriggerEventItem); - - new_event = (DeferredTriggerEvent) palloc(new_size); - new_event->dte_event = event; - new_event->dte_relid = rel->rd_id; - ItemPointerCopy(&oldctid, &(new_event->dte_oldctid)); - ItemPointerCopy(&newctid, &(new_event->dte_newctid)); - new_event->dte_n_items = ntriggers; - new_event->dte_item[ntriggers].dti_state = new_size; - for (i = 0; i < ntriggers; i++) - { - new_event->dte_item[i].dti_tgoid = triggers[i]->tgoid; - new_event->dte_item[i].dti_state = - ((triggers[i]->tgdeferrable) ? - TRIGGER_DEFERRED_DEFERRABLE : 0) | - ((triggers[i]->tginitdeferred) ? - TRIGGER_DEFERRED_INITDEFERRED : 0) | - ((rel->trigdesc->n_before_row[event] > 0) ? - TRIGGER_DEFERRED_HAS_BEFORE : 0); - } - deferredTriggerAddEvent(new_event); - MemoryContextSwitchTo(oldcxt); return; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 2162bbff31..afcbcce3ed 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -5,7 +5,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: analyze.c,v 1.126 1999/12/10 07:37:35 tgl Exp $ + * $Id: analyze.c,v 1.127 2000/01/06 20:46:49 wieck Exp $ * *------------------------------------------------------------------------- */ @@ -1015,141 +1015,141 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) extras_after = lappend(extras_after, (Node *)fk_trigger); - if ((fkconstraint->actions & FKCONSTR_ON_DELETE_MASK) != 0) + /* + * Build a CREATE CONSTRAINT TRIGGER statement for the + * ON DELETE action fired on the PK table !!! + * + */ + fk_trigger = (CreateTrigStmt *)makeNode(CreateTrigStmt); + fk_trigger->trigname = fkconstraint->constr_name; + fk_trigger->relname = fkconstraint->pktable_name; + switch ((fkconstraint->actions & FKCONSTR_ON_DELETE_MASK) + >> FKCONSTR_ON_DELETE_SHIFT) { - /* - * Build a CREATE CONSTRAINT TRIGGER statement for the - * ON DELETE action fired on the PK table !!! - * - */ - fk_trigger = (CreateTrigStmt *)makeNode(CreateTrigStmt); - fk_trigger->trigname = fkconstraint->constr_name; - fk_trigger->relname = fkconstraint->pktable_name; - switch ((fkconstraint->actions & FKCONSTR_ON_DELETE_MASK) - >> FKCONSTR_ON_DELETE_SHIFT) - { - case FKCONSTR_ON_KEY_RESTRICT: - fk_trigger->funcname = "RI_FKey_restrict_del"; - break; - case FKCONSTR_ON_KEY_CASCADE: - fk_trigger->funcname = "RI_FKey_cascade_del"; - break; - case FKCONSTR_ON_KEY_SETNULL: - fk_trigger->funcname = "RI_FKey_setnull_del"; - break; - case FKCONSTR_ON_KEY_SETDEFAULT: - fk_trigger->funcname = "RI_FKey_setdefault_del"; - break; - default: - elog(ERROR, "Only one ON DELETE action can be specified for FOREIGN KEY constraint"); - break; - } - fk_trigger->before = false; - fk_trigger->row = true; - fk_trigger->actions[0] = 'd'; - fk_trigger->actions[1] = '\0'; - fk_trigger->lang = NULL; - fk_trigger->text = NULL; - fk_trigger->attr = NIL; - fk_trigger->when = NULL; - fk_trigger->isconstraint = true; - fk_trigger->deferrable = fkconstraint->deferrable; - fk_trigger->initdeferred = fkconstraint->initdeferred; - fk_trigger->constrrelname = stmt->relname; - - fk_trigger->args = NIL; - fk_trigger->args = lappend(fk_trigger->args, - fkconstraint->constr_name); - fk_trigger->args = lappend(fk_trigger->args, - stmt->relname); - fk_trigger->args = lappend(fk_trigger->args, - fkconstraint->pktable_name); - fk_trigger->args = lappend(fk_trigger->args, - fkconstraint->match_type); - fk_attr = fkconstraint->fk_attrs; - pk_attr = fkconstraint->pk_attrs; - while (fk_attr != NIL) - { - id = (Ident *)lfirst(fk_attr); - fk_trigger->args = lappend(fk_trigger->args, id->name); + case FKCONSTR_ON_KEY_NOACTION: + fk_trigger->funcname = "RI_FKey_noaction_del"; + break; + case FKCONSTR_ON_KEY_RESTRICT: + fk_trigger->funcname = "RI_FKey_restrict_del"; + break; + case FKCONSTR_ON_KEY_CASCADE: + fk_trigger->funcname = "RI_FKey_cascade_del"; + break; + case FKCONSTR_ON_KEY_SETNULL: + fk_trigger->funcname = "RI_FKey_setnull_del"; + break; + case FKCONSTR_ON_KEY_SETDEFAULT: + fk_trigger->funcname = "RI_FKey_setdefault_del"; + break; + default: + elog(ERROR, "Only one ON DELETE action can be specified for FOREIGN KEY constraint"); + break; + } + fk_trigger->before = false; + fk_trigger->row = true; + fk_trigger->actions[0] = 'd'; + fk_trigger->actions[1] = '\0'; + fk_trigger->lang = NULL; + fk_trigger->text = NULL; + fk_trigger->attr = NIL; + fk_trigger->when = NULL; + fk_trigger->isconstraint = true; + fk_trigger->deferrable = fkconstraint->deferrable; + fk_trigger->initdeferred = fkconstraint->initdeferred; + fk_trigger->constrrelname = stmt->relname; - id = (Ident *)lfirst(pk_attr); - fk_trigger->args = lappend(fk_trigger->args, id->name); + fk_trigger->args = NIL; + fk_trigger->args = lappend(fk_trigger->args, + fkconstraint->constr_name); + fk_trigger->args = lappend(fk_trigger->args, + stmt->relname); + fk_trigger->args = lappend(fk_trigger->args, + fkconstraint->pktable_name); + fk_trigger->args = lappend(fk_trigger->args, + fkconstraint->match_type); + fk_attr = fkconstraint->fk_attrs; + pk_attr = fkconstraint->pk_attrs; + while (fk_attr != NIL) + { + id = (Ident *)lfirst(fk_attr); + fk_trigger->args = lappend(fk_trigger->args, id->name); - fk_attr = lnext(fk_attr); - pk_attr = lnext(pk_attr); - } + id = (Ident *)lfirst(pk_attr); + fk_trigger->args = lappend(fk_trigger->args, id->name); - extras_after = lappend(extras_after, (Node *)fk_trigger); + fk_attr = lnext(fk_attr); + pk_attr = lnext(pk_attr); } - if ((fkconstraint->actions & FKCONSTR_ON_UPDATE_MASK) != 0) + extras_after = lappend(extras_after, (Node *)fk_trigger); + + /* + * Build a CREATE CONSTRAINT TRIGGER statement for the + * ON UPDATE action fired on the PK table !!! + * + */ + fk_trigger = (CreateTrigStmt *)makeNode(CreateTrigStmt); + fk_trigger->trigname = fkconstraint->constr_name; + fk_trigger->relname = fkconstraint->pktable_name; + switch ((fkconstraint->actions & FKCONSTR_ON_UPDATE_MASK) + >> FKCONSTR_ON_UPDATE_SHIFT) { - /* - * Build a CREATE CONSTRAINT TRIGGER statement for the - * ON UPDATE action fired on the PK table !!! - * - */ - fk_trigger = (CreateTrigStmt *)makeNode(CreateTrigStmt); - fk_trigger->trigname = fkconstraint->constr_name; - fk_trigger->relname = fkconstraint->pktable_name; - switch ((fkconstraint->actions & FKCONSTR_ON_UPDATE_MASK) - >> FKCONSTR_ON_UPDATE_SHIFT) - { - case FKCONSTR_ON_KEY_RESTRICT: - fk_trigger->funcname = "RI_FKey_restrict_upd"; - break; - case FKCONSTR_ON_KEY_CASCADE: - fk_trigger->funcname = "RI_FKey_cascade_upd"; - break; - case FKCONSTR_ON_KEY_SETNULL: - fk_trigger->funcname = "RI_FKey_setnull_upd"; - break; - case FKCONSTR_ON_KEY_SETDEFAULT: - fk_trigger->funcname = "RI_FKey_setdefault_upd"; - break; - default: - elog(ERROR, "Only one ON UPDATE action can be specified for FOREIGN KEY constraint"); - break; - } - fk_trigger->before = false; - fk_trigger->row = true; - fk_trigger->actions[0] = 'u'; - fk_trigger->actions[1] = '\0'; - fk_trigger->lang = NULL; - fk_trigger->text = NULL; - fk_trigger->attr = NIL; - fk_trigger->when = NULL; - fk_trigger->isconstraint = true; - fk_trigger->deferrable = fkconstraint->deferrable; - fk_trigger->initdeferred = fkconstraint->initdeferred; - fk_trigger->constrrelname = stmt->relname; - - fk_trigger->args = NIL; - fk_trigger->args = lappend(fk_trigger->args, - fkconstraint->constr_name); - fk_trigger->args = lappend(fk_trigger->args, - stmt->relname); - fk_trigger->args = lappend(fk_trigger->args, - fkconstraint->pktable_name); - fk_trigger->args = lappend(fk_trigger->args, - fkconstraint->match_type); - fk_attr = fkconstraint->fk_attrs; - pk_attr = fkconstraint->pk_attrs; - while (fk_attr != NIL) - { - id = (Ident *)lfirst(fk_attr); - fk_trigger->args = lappend(fk_trigger->args, id->name); + case FKCONSTR_ON_KEY_NOACTION: + fk_trigger->funcname = "RI_FKey_noaction_upd"; + break; + case FKCONSTR_ON_KEY_RESTRICT: + fk_trigger->funcname = "RI_FKey_restrict_upd"; + break; + case FKCONSTR_ON_KEY_CASCADE: + fk_trigger->funcname = "RI_FKey_cascade_upd"; + break; + case FKCONSTR_ON_KEY_SETNULL: + fk_trigger->funcname = "RI_FKey_setnull_upd"; + break; + case FKCONSTR_ON_KEY_SETDEFAULT: + fk_trigger->funcname = "RI_FKey_setdefault_upd"; + break; + default: + elog(ERROR, "Only one ON UPDATE action can be specified for FOREIGN KEY constraint"); + break; + } + fk_trigger->before = false; + fk_trigger->row = true; + fk_trigger->actions[0] = 'u'; + fk_trigger->actions[1] = '\0'; + fk_trigger->lang = NULL; + fk_trigger->text = NULL; + fk_trigger->attr = NIL; + fk_trigger->when = NULL; + fk_trigger->isconstraint = true; + fk_trigger->deferrable = fkconstraint->deferrable; + fk_trigger->initdeferred = fkconstraint->initdeferred; + fk_trigger->constrrelname = stmt->relname; - id = (Ident *)lfirst(pk_attr); - fk_trigger->args = lappend(fk_trigger->args, id->name); + fk_trigger->args = NIL; + fk_trigger->args = lappend(fk_trigger->args, + fkconstraint->constr_name); + fk_trigger->args = lappend(fk_trigger->args, + stmt->relname); + fk_trigger->args = lappend(fk_trigger->args, + fkconstraint->pktable_name); + fk_trigger->args = lappend(fk_trigger->args, + fkconstraint->match_type); + fk_attr = fkconstraint->fk_attrs; + pk_attr = fkconstraint->pk_attrs; + while (fk_attr != NIL) + { + id = (Ident *)lfirst(fk_attr); + fk_trigger->args = lappend(fk_trigger->args, id->name); - fk_attr = lnext(fk_attr); - pk_attr = lnext(pk_attr); - } + id = (Ident *)lfirst(pk_attr); + fk_trigger->args = lappend(fk_trigger->args, id->name); - extras_after = lappend(extras_after, (Node *)fk_trigger); + fk_attr = lnext(fk_attr); + pk_attr = lnext(pk_attr); } + + extras_after = lappend(extras_after, (Node *)fk_trigger); } } diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index a5c7fe8b32..e2b38821d8 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -6,7 +6,7 @@ * * 1999 Jan Wieck * - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.11 2000/01/06 16:30:43 wieck Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.12 2000/01/06 20:46:51 wieck Exp $ * * ---------- */ @@ -466,6 +466,34 @@ RI_FKey_check_upd (FmgrInfo *proinfo) } +/* ---------- + * RI_FKey_noaction_del - + * + * This is here only to let the trigger manager trace for + * "triggered data change violation" + * ---------- + */ +HeapTuple +RI_FKey_noaction_del (FmgrInfo *proinfo) +{ + return NULL; +} + + +/* ---------- + * RI_FKey_noaction_upd - + * + * This is here only to let the trigger manager trace for + * "triggered data change violation" + * ---------- + */ +HeapTuple +RI_FKey_noaction_upd (FmgrInfo *proinfo) +{ + return NULL; +} + + /* ---------- * RI_FKey_cascade_del - * @@ -2252,6 +2280,101 @@ RI_FKey_setdefault_upd (FmgrInfo *proinfo) } +/* ---------- + * RI_FKey_keyequal_upd - + * + * Check if we have a key change on update. + * + * This is no real trigger procedure. It is used by the deferred + * trigger queue manager to detect "triggered data change violation". + * ---------- + */ +bool +RI_FKey_keyequal_upd (void) +{ + TriggerData *trigdata; + int tgnargs; + char **tgargs; + Relation fk_rel; + Relation pk_rel; + HeapTuple new_row; + HeapTuple old_row; + RI_QueryKey qkey; + + trigdata = CurrentTriggerData; + CurrentTriggerData = NULL; + + /* ---------- + * Check for the correct # of call arguments + * ---------- + */ + tgnargs = trigdata->tg_trigger->tgnargs; + tgargs = trigdata->tg_trigger->tgargs; + if (tgnargs < 4 || (tgnargs % 2) != 0) + elog(ERROR, "wrong # of arguments in call to RI_FKey_keyequal_upd()"); + if (tgnargs > RI_MAX_ARGUMENTS) + elog(ERROR, "too many keys (%d max) in call to RI_FKey_keyequal_upd()", + RI_MAX_NUMKEYS); + + /* ---------- + * Nothing to do if no column names to compare given + * ---------- + */ + if (tgnargs == 4) + return true; + + /* ---------- + * Get the relation descriptors of the FK and PK tables and + * the new and old tuple. + * ---------- + */ + fk_rel = heap_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock); + pk_rel = trigdata->tg_relation; + new_row = trigdata->tg_newtuple; + old_row = trigdata->tg_trigtuple; + + switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO])) + { + /* ---------- + * MATCH + * ---------- + */ + case RI_MATCH_TYPE_UNSPECIFIED: + elog(ERROR, "MATCH not implemented yet"); + break; + + case RI_MATCH_TYPE_FULL: + ri_BuildQueryKeyFull(&qkey, trigdata->tg_trigger->tgoid, + 0, + fk_rel, pk_rel, + tgnargs, tgargs); + heap_close(fk_rel, NoLock); + + /* ---------- + * 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: + elog(ERROR, "MATCH PARTIAL not yet supported"); + break; + } + + /* ---------- + * Never reached + * ---------- + */ + elog(ERROR, "internal error #9 in ri_triggers.c"); + return false; +} + + diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index be62e108d4..abb03c76f8 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: pg_proc.h,v 1.110 1999/12/28 13:40:50 wieck Exp $ + * $Id: pg_proc.h,v 1.111 2000/01/06 20:46:54 wieck Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -2142,6 +2142,10 @@ DATA(insert OID = 1652 ( RI_FKey_setdefault_del PGUID 11 f t f 0 f 0 "" 100 0 0 DESCR("referential integrity ON DELETE SET DEFAULT"); DATA(insert OID = 1653 ( RI_FKey_setdefault_upd PGUID 11 f t f 0 f 0 "" 100 0 0 100 RI_FKey_setdefault_upd - )); DESCR("referential integrity ON UPDATE SET DEFAULT"); +DATA(insert OID = 1654 ( RI_FKey_noaction_del PGUID 11 f t f 0 f 0 "" 100 0 0 100 RI_FKey_setdefault_del - )); +DESCR("referential integrity ON DELETE NO ACTION"); +DATA(insert OID = 1655 ( RI_FKey_noaction_upd PGUID 11 f t f 0 f 0 "" 100 0 0 100 RI_FKey_setdefault_upd - )); +DESCR("referential integrity ON UPDATE NO ACTION"); /* for mac type support */ DATA(insert OID = 436 ( macaddr_in PGUID 11 f t t 1 f 829 "0" 100 0 0 100 macaddr_in - )); diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h index 8ab4342fa7..ddb9f7ca5b 100644 --- a/src/include/commands/trigger.h +++ b/src/include/commands/trigger.h @@ -37,7 +37,9 @@ extern DLLIMPORT TriggerData *CurrentTriggerData; #define TRIGGER_DEFERRED_DEFERRABLE 0x00000040 #define TRIGGER_DEFERRED_INITDEFERRED 0x00000080 #define TRIGGER_DEFERRED_HAS_BEFORE 0x00000100 -#define TRIGGER_DEFERRED_MASK 0x000001F0 +#define TRIGGER_DEFERRED_ROW_INSERTED 0x00000200 +#define TRIGGER_DEFERRED_KEY_CHANGED 0x00000400 +#define TRIGGER_DEFERRED_MASK 0x000007F0 #define TRIGGER_FIRED_BY_INSERT(event) \ (((TriggerEvent) (event) & TRIGGER_EVENT_OPMASK) == \ @@ -117,4 +119,10 @@ extern void DeferredTriggerSaveEvent(Relation rel, int event, HeapTuple oldtup, HeapTuple newtup); +/* + * in utils/adt/ri_triggers.c + * + */ +extern bool RI_FKey_keyequal_upd(void); + #endif /* TRIGGER_H */ diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 4486131d41..eca5f42d33 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: builtins.h,v 1.93 1999/12/28 13:40:52 wieck Exp $ + * $Id: builtins.h,v 1.94 2000/01/06 20:47:01 wieck Exp $ * * NOTES * This should normally only be included by fmgr.h. @@ -623,6 +623,8 @@ float64 numeric_float8(Numeric num); /* ri_triggers.c */ HeapTuple RI_FKey_check_ins(FmgrInfo *proinfo); HeapTuple RI_FKey_check_upd(FmgrInfo *proinfo); +HeapTuple RI_FKey_noaction_del(FmgrInfo *proinfo); +HeapTuple RI_FKey_noaction_upd(FmgrInfo *proinfo); HeapTuple RI_FKey_cascade_del(FmgrInfo *proinfo); HeapTuple RI_FKey_cascade_upd(FmgrInfo *proinfo); HeapTuple RI_FKey_restrict_del(FmgrInfo *proinfo); -- 2.40.0