diff options
author | Jan Wieck <JanWieck@Yahoo.com> | 2000-01-06 20:47:01 +0000 |
---|---|---|
committer | Jan Wieck <JanWieck@Yahoo.com> | 2000-01-06 20:47:01 +0000 |
commit | b7b6d4bf532512bea7ceb46b6250f9f617948ef4 (patch) | |
tree | f299c2740c569369c60878c1f7c3995fe83048f5 /src/backend/commands/trigger.c | |
parent | 88016a564a20276472f02a2fd98ec9fc7d968c4f (diff) | |
download | postgresql-b7b6d4bf532512bea7ceb46b6250f9f617948ef4.tar.gz postgresql-b7b6d4bf532512bea7ceb46b6250f9f617948ef4.zip |
Changed "triggered data change violation" detection code
in trigger manager.
Jan
Diffstat (limited to 'src/backend/commands/trigger.c')
-rw-r--r-- | src/backend/commands/trigger.c | 326 |
1 files changed, 161 insertions, 165 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index daa5b8e7175..b0196c90dee 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -1055,20 +1055,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() * * Fetch the required tuples back from the heap and fire one @@ -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; |