mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-30 19:00:29 +08:00
Changed "triggered data change violation" detection code
in trigger manager. Jan
This commit is contained in:
parent
88016a564a
commit
b7b6d4bf53
@ -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,172 +1681,7 @@ DeferredTriggerSaveEvent(Relation rel, int event,
|
||||
ItemPointerSetInvalid(&(newctid));
|
||||
|
||||
/* ----------
|
||||
* Eventually modify the event and do some general RI violation checks
|
||||
* ----------
|
||||
*/
|
||||
switch (event)
|
||||
{
|
||||
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.
|
||||
* ----------
|
||||
*/
|
||||
break;
|
||||
|
||||
case TRIGGER_EVENT_UPDATE:
|
||||
/* ----------
|
||||
* On UPDATE check if the tuple updated is a result
|
||||
* of the same 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.
|
||||
* ----------
|
||||
*/
|
||||
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, "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.");
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* 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 an INSERT of
|
||||
* the new tuple.
|
||||
* ----------
|
||||
*/
|
||||
event = TRIGGER_EVENT_INSERT;
|
||||
ItemPointerSetInvalid(&oldctid);
|
||||
deferredTriggerCancelEvent(prev_event);
|
||||
break;
|
||||
|
||||
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.
|
||||
* ----------
|
||||
*/
|
||||
event = TRIGGER_EVENT_UPDATE;
|
||||
ItemPointerCopy(&(prev_event->dte_oldctid), &oldctid);
|
||||
deferredTriggerCancelEvent(prev_event);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TRIGGER_EVENT_DELETE:
|
||||
/* ----------
|
||||
* On DELETE check if the tuple updated is a result
|
||||
* of the same 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.
|
||||
* ----------
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* Create a new event and save it.
|
||||
* Create a new event
|
||||
* ----------
|
||||
*/
|
||||
oldcxt = MemoryContextSwitchTo((MemoryContext) deftrig_cxt);
|
||||
@ -1871,7 +1693,7 @@ DeferredTriggerSaveEvent(Relation rel, int event,
|
||||
ntriggers * sizeof(DeferredTriggerEventItem);
|
||||
|
||||
new_event = (DeferredTriggerEvent) palloc(new_size);
|
||||
new_event->dte_event = event;
|
||||
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));
|
||||
@ -1888,9 +1710,183 @@ DeferredTriggerSaveEvent(Relation rel, int event,
|
||||
((rel->trigdesc->n_before_row[event] > 0) ?
|
||||
TRIGGER_DEFERRED_HAS_BEFORE : 0);
|
||||
}
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
switch (event & TRIGGER_EVENT_OPMASK)
|
||||
{
|
||||
case TRIGGER_EVENT_INSERT:
|
||||
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 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())
|
||||
prev_event = NULL;
|
||||
else
|
||||
prev_event =
|
||||
deferredTriggerGetPreviousEvent(rel->rd_id, &oldctid);
|
||||
|
||||
/* ----------
|
||||
* Now check if one of the referenced keys is changed.
|
||||
* ----------
|
||||
*/
|
||||
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)
|
||||
{
|
||||
case F_RI_FKEY_NOACTION_UPD:
|
||||
is_ri_trigger = true;
|
||||
new_event->dte_item[i].dti_state |=
|
||||
TRIGGER_DEFERRED_DONE;
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
{
|
||||
/* ----------
|
||||
* Bomb out if this key has been changed before.
|
||||
* Otherwise remember that we do so.
|
||||
* ----------
|
||||
*/
|
||||
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)));
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* This is the first change to this key, so let
|
||||
* it happen.
|
||||
* ----------
|
||||
*/
|
||||
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 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.
|
||||
* ----------
|
||||
*/
|
||||
prev_event = deferredTriggerGetPreviousEvent(rel->rd_id, &oldctid);
|
||||
if (prev_event->dte_event & TRIGGER_DEFERRED_KEY_CHANGED)
|
||||
elog(ERROR, "triggered data change violation "
|
||||
"on relation \"%s\"",
|
||||
nameout(&(rel->rd_rel->relname)));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* Anything's fine up to here. Add the new event to the queue.
|
||||
* ----------
|
||||
*/
|
||||
oldcxt = MemoryContextSwitchTo((MemoryContext) deftrig_cxt);
|
||||
deferredTriggerAddEvent(new_event);
|
||||
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
return;
|
||||
|
@ -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;
|
||||
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;
|
||||
|
||||
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_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 = lappend(fk_trigger->args, id->name);
|
||||
id = (Ident *)lfirst(pk_attr);
|
||||
fk_trigger->args = lappend(fk_trigger->args, id->name);
|
||||
|
||||
fk_attr = lnext(fk_attr);
|
||||
pk_attr = lnext(pk_attr);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
id = (Ident *)lfirst(pk_attr);
|
||||
fk_trigger->args = lappend(fk_trigger->args, id->name);
|
||||
|
||||
fk_attr = lnext(fk_attr);
|
||||
pk_attr = lnext(pk_attr);
|
||||
}
|
||||
|
||||
extras_after = lappend(extras_after, (Node *)fk_trigger);
|
||||
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;
|
||||
|
||||
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 = lappend(fk_trigger->args, id->name);
|
||||
|
||||
fk_attr = lnext(fk_attr);
|
||||
pk_attr = lnext(pk_attr);
|
||||
}
|
||||
|
||||
extras_after = lappend(extras_after, (Node *)fk_trigger);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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 - ));
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user