]> granicus.if.org Git - postgresql/commitdiff
Changed "triggered data change violation" detection code
authorJan Wieck <JanWieck@Yahoo.com>
Thu, 6 Jan 2000 20:47:01 +0000 (20:47 +0000)
committerJan Wieck <JanWieck@Yahoo.com>
Thu, 6 Jan 2000 20:47:01 +0000 (20:47 +0000)
in trigger manager.

Jan

src/backend/commands/trigger.c
src/backend/parser/analyze.c
src/backend/utils/adt/ri_triggers.c
src/include/catalog/pg_proc.h
src/include/commands/trigger.h
src/include/utils/builtins.h

index daa5b8e7175ae2f363dfc716a21d33a25e2188a6..b0196c90deeb45db1b23a2cee8dee722e6c1e641 100644 (file)
@@ -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;
index 2162bbff315c77b8121e422b38567be9b54be411..afcbcce3ed919fd5f8c9e61e24d78925997edc49 100644 (file)
@@ -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);
                }
        }
 
index a5c7fe8b321af8628a384fb3a4731cb5b77eb765..e2b38821d85a2f2c2fb8b4740190cfd8d562543e 100644 (file)
@@ -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 <UNSPECIFIED>
+                * ----------
+                */
+               case RI_MATCH_TYPE_UNSPECIFIED:
+                       elog(ERROR, "MATCH <unspecified> 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;
+}
+
+
 
 
 
index be62e108d4a545f1b164acb7940cd1b8afe7ac5c..abb03c76f8a9a6846db80b10c178421eb731f02c 100644 (file)
@@ -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 - ));
index 8ab4342fa7ae31d45eb1c90d48a961d9cdcd9cf5..ddb9f7ca5b45e160bdcd1f0ceb9058b56d745b25 100644 (file)
@@ -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 */
index 4486131d416abc7fa66ac628dafc6d1d3be035b6..eca5f42d33afb32427f3468f84e90828426c6fbc 100644 (file)
@@ -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);