aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/commands/trigger.c326
-rw-r--r--src/backend/parser/analyze.c250
-rw-r--r--src/backend/utils/adt/ri_triggers.c125
-rw-r--r--src/include/catalog/pg_proc.h6
-rw-r--r--src/include/commands/trigger.h10
-rw-r--r--src/include/utils/builtins.h4
6 files changed, 427 insertions, 294 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;
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 2162bbff315..afcbcce3ed9 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 a5c7fe8b321..e2b38821d85 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 $
*
* ----------
*/
@@ -467,6 +467,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 -
*
* Cascaded delete foreign key references at delete event on PK table.
@@ -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;
+}
+
+
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index be62e108d4a..abb03c76f8a 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 8ab4342fa7a..ddb9f7ca5b4 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 4486131d416..eca5f42d33a 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);