Commit b7b6d4bf authored by Jan Wieck's avatar Jan Wieck

Changed "triggered data change violation" detection code

in trigger manager.

Jan
parent 88016a56
...@@ -1054,20 +1054,6 @@ deferredTriggerGetPreviousEvent(Oid relid, ItemPointer ctid) ...@@ -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() * deferredTriggerExecute()
* *
...@@ -1112,10 +1098,11 @@ deferredTriggerExecute(DeferredTriggerEvent event, int itemno) ...@@ -1112,10 +1098,11 @@ deferredTriggerExecute(DeferredTriggerEvent event, int itemno)
* Setup the trigger information * 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; SaveTriggerData.tg_relation = rel;
switch (event->dte_event) switch (event->dte_event & TRIGGER_EVENT_OPMASK)
{ {
case TRIGGER_EVENT_INSERT: case TRIGGER_EVENT_INSERT:
SaveTriggerData.tg_trigtuple = &newtuple; SaveTriggerData.tg_trigtuple = &newtuple;
...@@ -1656,13 +1643,13 @@ DeferredTriggerSaveEvent(Relation rel, int event, ...@@ -1656,13 +1643,13 @@ DeferredTriggerSaveEvent(Relation rel, int event,
MemoryContext oldcxt; MemoryContext oldcxt;
DeferredTriggerEvent new_event; DeferredTriggerEvent new_event;
DeferredTriggerEvent prev_event; DeferredTriggerEvent prev_event;
bool prev_done = false;
int new_size; int new_size;
int i; int i;
int ntriggers; int ntriggers;
Trigger **triggers; Trigger **triggers;
ItemPointerData oldctid; ItemPointerData oldctid;
ItemPointerData newctid; ItemPointerData newctid;
TriggerData SaveTriggerData;
if (deftrig_cxt == NULL) if (deftrig_cxt == NULL)
elog(ERROR, elog(ERROR,
...@@ -1694,203 +1681,212 @@ DeferredTriggerSaveEvent(Relation rel, int event, ...@@ -1694,203 +1681,212 @@ DeferredTriggerSaveEvent(Relation rel, int event,
ItemPointerSetInvalid(&(newctid)); 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: case TRIGGER_EVENT_INSERT:
/* ---------- new_event->dte_event |= TRIGGER_DEFERRED_ROW_INSERTED;
* Don't know how to (surely) check if another tuple with new_event->dte_event |= TRIGGER_DEFERRED_KEY_CHANGED;
* this meaning (from all FK's point of view) got deleted
* in the same transaction. Thus not handled yet.
* ----------
*/
break; break;
case TRIGGER_EVENT_UPDATE: case TRIGGER_EVENT_UPDATE:
/* ---------- /* ----------
* On UPDATE check if the tuple updated is a result * On UPDATE check if the tuple updated has been inserted
* of the same transaction. * 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()) 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 * Now check if one of the referenced keys is changed.
* any of its triggers has already been executed.
* Such a situation would potentially violate RI
* so we abort the transaction.
* ---------- * ----------
*/ */
prev_event = deferredTriggerGetPreviousEvent(rel->rd_id, &oldctid); for (i = 0; i < ntriggers; i++)
if (prev_event->dte_event & TRIGGER_DEFERRED_HAS_BEFORE || {
(prev_event->dte_n_items != 0 && bool is_ri_trigger;
prev_event->dte_event & TRIGGER_DEFERRED_DONE)) bool key_unchanged;
prev_done = true;
else /* ----------
for (i = 0; i < prev_event->dte_n_items; i++) * We are interested in RI_FKEY triggers only.
* ----------
*/
switch (triggers[i]->tgfoid)
{ {
if (prev_event->dte_item[i].dti_state & case F_RI_FKEY_NOACTION_UPD:
TRIGGER_DEFERRED_DONE) is_ri_trigger = true;
{ new_event->dte_item[i].dti_state |=
prev_done = true; TRIGGER_DEFERRED_DONE;
break; break;
}
}
if (prev_done) case F_RI_FKEY_CASCADE_UPD:
{ case F_RI_FKEY_RESTRICT_UPD:
elog(NOTICE, "UPDATE of row inserted/updated in same " case F_RI_FKEY_SETNULL_UPD:
"transaction violates"); case F_RI_FKEY_SETDEFAULT_UPD:
elog(NOTICE, "referential integrity semantics. Other " is_ri_trigger = true;
"triggers or IMMEDIATE "); break;
elog(ERROR, " constraints have already been executed.");
} 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;
/* ---------- if (prev_event)
* Anything's fine so far - i.e. none of the previous {
* events triggers has been executed up to now. Let's if (prev_event->dte_event &
* the REAL event that happened so far. TRIGGER_DEFERRED_ROW_INSERTED)
* ---------- {
*/ /* ----------
switch (prev_event->dte_event & TRIGGER_EVENT_OPMASK) * This is a row inserted during our transaction.
{ * So any key value is considered changed.
case TRIGGER_EVENT_INSERT: * ----------
*/
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. * Bomb out if this key has been changed before.
* So the REAL new event is an INSERT of * Otherwise remember that we do so.
* the new tuple.
* ---------- * ----------
*/ */
event = TRIGGER_EVENT_INSERT; if (prev_event)
ItemPointerSetInvalid(&oldctid); {
deferredTriggerCancelEvent(prev_event); if (prev_event->dte_event &
break; 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. * This is the first change to this key, so let
* So the REAL new event is still an UPDATE * it happen.
* but from the original tuple to this new one.
* ---------- * ----------
*/ */
event = TRIGGER_EVENT_UPDATE; new_event->dte_item[i].dti_state |=
ItemPointerCopy(&(prev_event->dte_oldctid), &oldctid); TRIGGER_DEFERRED_KEY_CHANGED;
deferredTriggerCancelEvent(prev_event); new_event->dte_event |= TRIGGER_DEFERRED_KEY_CHANGED;
break; }
} }
break; break;
case TRIGGER_EVENT_DELETE: case TRIGGER_EVENT_DELETE:
/* ---------- /* ----------
* On DELETE check if the tuple updated is a result * On DELETE check if the tuple deleted has been inserted
* of the same transaction. * or a possibly referenced key value has changed in this
* transaction.
* ---------- * ----------
*/ */
if (oldtup->t_data->t_xmin != GetCurrentTransactionId()) if (oldtup->t_data->t_xmin != GetCurrentTransactionId())
break; break;
/* ---------- /* ----------
* Look at the previous event to the same tuple if * Look at the previous event to the same tuple.
* any of its triggers has already been executed.
* Such a situation would potentially violate RI
* so we abort the transaction.
* ---------- * ----------
*/ */
prev_event = deferredTriggerGetPreviousEvent(rel->rd_id, &oldctid); prev_event = deferredTriggerGetPreviousEvent(rel->rd_id, &oldctid);
if (prev_event->dte_event & TRIGGER_DEFERRED_HAS_BEFORE || if (prev_event->dte_event & TRIGGER_DEFERRED_KEY_CHANGED)
(prev_event->dte_n_items != 0 && elog(ERROR, "triggered data change violation "
prev_event->dte_event & TRIGGER_DEFERRED_DONE)) "on relation \"%s\"",
prev_done = true; nameout(&(rel->rd_rel->relname)));
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;
}
break; 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); 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); deferredTriggerAddEvent(new_event);
MemoryContextSwitchTo(oldcxt); MemoryContextSwitchTo(oldcxt);
return; return;
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * 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) ...@@ -1015,141 +1015,141 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
extras_after = lappend(extras_after, (Node *)fk_trigger); 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)
{ {
/* case FKCONSTR_ON_KEY_NOACTION:
* Build a CREATE CONSTRAINT TRIGGER statement for the fk_trigger->funcname = "RI_FKey_noaction_del";
* ON DELETE action fired on the PK table !!! break;
* case FKCONSTR_ON_KEY_RESTRICT:
*/ fk_trigger->funcname = "RI_FKey_restrict_del";
fk_trigger = (CreateTrigStmt *)makeNode(CreateTrigStmt); break;
fk_trigger->trigname = fkconstraint->constr_name; case FKCONSTR_ON_KEY_CASCADE:
fk_trigger->relname = fkconstraint->pktable_name; fk_trigger->funcname = "RI_FKey_cascade_del";
switch ((fkconstraint->actions & FKCONSTR_ON_DELETE_MASK) break;
>> FKCONSTR_ON_DELETE_SHIFT) case FKCONSTR_ON_KEY_SETNULL:
{ fk_trigger->funcname = "RI_FKey_setnull_del";
case FKCONSTR_ON_KEY_RESTRICT: break;
fk_trigger->funcname = "RI_FKey_restrict_del"; case FKCONSTR_ON_KEY_SETDEFAULT:
break; fk_trigger->funcname = "RI_FKey_setdefault_del";
case FKCONSTR_ON_KEY_CASCADE: break;
fk_trigger->funcname = "RI_FKey_cascade_del"; default:
break; elog(ERROR, "Only one ON DELETE action can be specified for FOREIGN KEY constraint");
case FKCONSTR_ON_KEY_SETNULL: break;
fk_trigger->funcname = "RI_FKey_setnull_del"; }
break; fk_trigger->before = false;
case FKCONSTR_ON_KEY_SETDEFAULT: fk_trigger->row = true;
fk_trigger->funcname = "RI_FKey_setdefault_del"; fk_trigger->actions[0] = 'd';
break; fk_trigger->actions[1] = '\0';
default: fk_trigger->lang = NULL;
elog(ERROR, "Only one ON DELETE action can be specified for FOREIGN KEY constraint"); fk_trigger->text = NULL;
break; fk_trigger->attr = NIL;
} fk_trigger->when = NULL;
fk_trigger->before = false; fk_trigger->isconstraint = true;
fk_trigger->row = true; fk_trigger->deferrable = fkconstraint->deferrable;
fk_trigger->actions[0] = 'd'; fk_trigger->initdeferred = fkconstraint->initdeferred;
fk_trigger->actions[1] = '\0'; fk_trigger->constrrelname = stmt->relname;
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);
id = (Ident *)lfirst(pk_attr); fk_trigger->args = NIL;
fk_trigger->args = lappend(fk_trigger->args, id->name); 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); id = (Ident *)lfirst(pk_attr);
pk_attr = lnext(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)
{ {
/* case FKCONSTR_ON_KEY_NOACTION:
* Build a CREATE CONSTRAINT TRIGGER statement for the fk_trigger->funcname = "RI_FKey_noaction_upd";
* ON UPDATE action fired on the PK table !!! break;
* case FKCONSTR_ON_KEY_RESTRICT:
*/ fk_trigger->funcname = "RI_FKey_restrict_upd";
fk_trigger = (CreateTrigStmt *)makeNode(CreateTrigStmt); break;
fk_trigger->trigname = fkconstraint->constr_name; case FKCONSTR_ON_KEY_CASCADE:
fk_trigger->relname = fkconstraint->pktable_name; fk_trigger->funcname = "RI_FKey_cascade_upd";
switch ((fkconstraint->actions & FKCONSTR_ON_UPDATE_MASK) break;
>> FKCONSTR_ON_UPDATE_SHIFT) case FKCONSTR_ON_KEY_SETNULL:
{ fk_trigger->funcname = "RI_FKey_setnull_upd";
case FKCONSTR_ON_KEY_RESTRICT: break;
fk_trigger->funcname = "RI_FKey_restrict_upd"; case FKCONSTR_ON_KEY_SETDEFAULT:
break; fk_trigger->funcname = "RI_FKey_setdefault_upd";
case FKCONSTR_ON_KEY_CASCADE: break;
fk_trigger->funcname = "RI_FKey_cascade_upd"; default:
break; elog(ERROR, "Only one ON UPDATE action can be specified for FOREIGN KEY constraint");
case FKCONSTR_ON_KEY_SETNULL: break;
fk_trigger->funcname = "RI_FKey_setnull_upd"; }
break; fk_trigger->before = false;
case FKCONSTR_ON_KEY_SETDEFAULT: fk_trigger->row = true;
fk_trigger->funcname = "RI_FKey_setdefault_upd"; fk_trigger->actions[0] = 'u';
break; fk_trigger->actions[1] = '\0';
default: fk_trigger->lang = NULL;
elog(ERROR, "Only one ON UPDATE action can be specified for FOREIGN KEY constraint"); fk_trigger->text = NULL;
break; fk_trigger->attr = NIL;
} fk_trigger->when = NULL;
fk_trigger->before = false; fk_trigger->isconstraint = true;
fk_trigger->row = true; fk_trigger->deferrable = fkconstraint->deferrable;
fk_trigger->actions[0] = 'u'; fk_trigger->initdeferred = fkconstraint->initdeferred;
fk_trigger->actions[1] = '\0'; fk_trigger->constrrelname = stmt->relname;
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);
id = (Ident *)lfirst(pk_attr); fk_trigger->args = NIL;
fk_trigger->args = lappend(fk_trigger->args, id->name); 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); id = (Ident *)lfirst(pk_attr);
pk_attr = lnext(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);
} }
} }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* 1999 Jan Wieck * 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) ...@@ -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 - * RI_FKey_cascade_del -
* *
...@@ -2252,6 +2280,101 @@ RI_FKey_setdefault_upd (FmgrInfo *proinfo) ...@@ -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;
}
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * 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 * NOTES
* The script catalog/genbki.sh reads this file and generates .bki * 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 ...@@ -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"); 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 - )); 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"); 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 */ /* 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 - )); DATA(insert OID = 436 ( macaddr_in PGUID 11 f t t 1 f 829 "0" 100 0 0 100 macaddr_in - ));
......
...@@ -37,7 +37,9 @@ extern DLLIMPORT TriggerData *CurrentTriggerData; ...@@ -37,7 +37,9 @@ extern DLLIMPORT TriggerData *CurrentTriggerData;
#define TRIGGER_DEFERRED_DEFERRABLE 0x00000040 #define TRIGGER_DEFERRED_DEFERRABLE 0x00000040
#define TRIGGER_DEFERRED_INITDEFERRED 0x00000080 #define TRIGGER_DEFERRED_INITDEFERRED 0x00000080
#define TRIGGER_DEFERRED_HAS_BEFORE 0x00000100 #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) \ #define TRIGGER_FIRED_BY_INSERT(event) \
(((TriggerEvent) (event) & TRIGGER_EVENT_OPMASK) == \ (((TriggerEvent) (event) & TRIGGER_EVENT_OPMASK) == \
...@@ -117,4 +119,10 @@ extern void DeferredTriggerSaveEvent(Relation rel, int event, ...@@ -117,4 +119,10 @@ extern void DeferredTriggerSaveEvent(Relation rel, int event,
HeapTuple oldtup, HeapTuple newtup); HeapTuple oldtup, HeapTuple newtup);
/*
* in utils/adt/ri_triggers.c
*
*/
extern bool RI_FKey_keyequal_upd(void);
#endif /* TRIGGER_H */ #endif /* TRIGGER_H */
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * 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 * NOTES
* This should normally only be included by fmgr.h. * This should normally only be included by fmgr.h.
...@@ -623,6 +623,8 @@ float64 numeric_float8(Numeric num); ...@@ -623,6 +623,8 @@ float64 numeric_float8(Numeric num);
/* ri_triggers.c */ /* ri_triggers.c */
HeapTuple RI_FKey_check_ins(FmgrInfo *proinfo); HeapTuple RI_FKey_check_ins(FmgrInfo *proinfo);
HeapTuple RI_FKey_check_upd(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_del(FmgrInfo *proinfo);
HeapTuple RI_FKey_cascade_upd(FmgrInfo *proinfo); HeapTuple RI_FKey_cascade_upd(FmgrInfo *proinfo);
HeapTuple RI_FKey_restrict_del(FmgrInfo *proinfo); HeapTuple RI_FKey_restrict_del(FmgrInfo *proinfo);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment