Commit 7c50767f authored by Tom Lane's avatar Tom Lane

Remove 'triggered data change violation' error check, per recent

discussions in pghackers.
parent 306798de
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.98 2001/11/12 00:00:55 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.99 2001/11/16 16:31:16 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -935,10 +935,7 @@ ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, ...@@ -935,10 +935,7 @@ ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
{ {
TriggerDesc *trigdesc = relinfo->ri_TrigDesc; TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
/* Must save info if there are any deferred triggers on this rel */ if (trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0)
if (trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0 ||
trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0 ||
trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0)
DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT, DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT,
NULL, trigtuple); NULL, trigtuple);
} }
...@@ -1000,9 +997,7 @@ ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ...@@ -1000,9 +997,7 @@ ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
{ {
TriggerDesc *trigdesc = relinfo->ri_TrigDesc; TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
/* Must save info if there are upd/del deferred triggers on this rel */ if (trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0)
if (trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0 ||
trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0)
{ {
HeapTuple trigtuple = GetTupleForTrigger(estate, relinfo, HeapTuple trigtuple = GetTupleForTrigger(estate, relinfo,
tupleid, NULL); tupleid, NULL);
...@@ -1077,9 +1072,7 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, ...@@ -1077,9 +1072,7 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
{ {
TriggerDesc *trigdesc = relinfo->ri_TrigDesc; TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
/* Must save info if there are upd/del deferred triggers on this rel */ if (trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0)
if (trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0 ||
trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0)
{ {
HeapTuple trigtuple = GetTupleForTrigger(estate, relinfo, HeapTuple trigtuple = GetTupleForTrigger(estate, relinfo,
tupleid, NULL); tupleid, NULL);
...@@ -1208,17 +1201,17 @@ static bool deftrig_all_isdeferred; ...@@ -1208,17 +1201,17 @@ static bool deftrig_all_isdeferred;
static List *deftrig_trigstates; static List *deftrig_trigstates;
/* ---------- /* ----------
* The list of events during the entire transaction. deftrig_events * The list of pending deferred trigger events during the current transaction.
* is the head, deftrig_event_tail is the last entry. Because this can *
* grow pretty large, we don't use separate List nodes, but instead thread * deftrig_events is the head, deftrig_event_tail is the last entry.
* the list through the dte_next fields of the member nodes. Saves just a * Because this can grow pretty large, we don't use separate List nodes,
* few bytes per entry, but that adds up. * but instead thread the list through the dte_next fields of the member
* nodes. Saves just a few bytes per entry, but that adds up.
* *
* XXX Need to be able to shove this data out to a file if it grows too * XXX Need to be able to shove this data out to a file if it grows too
* large... * large...
* ---------- * ----------
*/ */
static int deftrig_n_events;
static DeferredTriggerEvent deftrig_events; static DeferredTriggerEvent deftrig_events;
static DeferredTriggerEvent deftrig_event_tail; static DeferredTriggerEvent deftrig_event_tail;
...@@ -1284,7 +1277,6 @@ deferredTriggerCheckState(Oid tgoid, int32 itemstate) ...@@ -1284,7 +1277,6 @@ deferredTriggerCheckState(Oid tgoid, int32 itemstate)
* deferredTriggerAddEvent() * deferredTriggerAddEvent()
* *
* Add a new trigger event to the queue. * Add a new trigger event to the queue.
* Caller must have switched into appropriate memory context!
* ---------- * ----------
*/ */
static void static void
...@@ -1307,44 +1299,6 @@ deferredTriggerAddEvent(DeferredTriggerEvent event) ...@@ -1307,44 +1299,6 @@ deferredTriggerAddEvent(DeferredTriggerEvent event)
deftrig_event_tail->dte_next = event; deftrig_event_tail->dte_next = event;
deftrig_event_tail = event; deftrig_event_tail = event;
} }
deftrig_n_events++;
}
/* ----------
* deferredTriggerGetPreviousEvent()
*
* Scan the eventlist to find the event a given OLD tuple
* resulted from in the same transaction.
* ----------
*/
static DeferredTriggerEvent
deferredTriggerGetPreviousEvent(Oid relid, ItemPointer ctid)
{
DeferredTriggerEvent previous = NULL;
DeferredTriggerEvent prev;
/* Search the list to find the last event affecting this tuple */
for (prev = deftrig_events; prev != NULL; prev = prev->dte_next)
{
if (prev->dte_relid != relid)
continue;
if (prev->dte_event & TRIGGER_DEFERRED_CANCELED)
continue;
if (ItemPointerGetBlockNumber(ctid) ==
ItemPointerGetBlockNumber(&(prev->dte_newctid)) &&
ItemPointerGetOffsetNumber(ctid) ==
ItemPointerGetOffsetNumber(&(prev->dte_newctid)))
previous = prev;
}
if (previous == NULL)
elog(ERROR,
"deferredTriggerGetPreviousEvent: event for tuple %s not found",
DatumGetCString(DirectFunctionCall1(tidout,
PointerGetDatum(ctid))));
return previous;
} }
...@@ -1473,18 +1427,25 @@ DeferredTriggerExecute(DeferredTriggerEvent event, int itemno, ...@@ -1473,18 +1427,25 @@ DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
static void static void
deferredTriggerInvokeEvents(bool immediate_only) deferredTriggerInvokeEvents(bool immediate_only)
{ {
DeferredTriggerEvent event; DeferredTriggerEvent event,
prev_event = NULL;
MemoryContext per_tuple_context; MemoryContext per_tuple_context;
Relation rel = NULL; Relation rel = NULL;
FmgrInfo *finfo = NULL; FmgrInfo *finfo = NULL;
/* /*
* For now we process all events - to speedup transaction blocks we * If immediate_only is true, we remove fully-processed events from
* need to remember the actual end of the queue at EndQuery and * the event queue to recycle space. If immediate_only is false,
* process only events that are newer. On state changes we simply * we are going to discard the whole event queue on return anyway,
* reset the position to the beginning of the queue and process all * so no need to bother with "retail" pfree's.
* events once with the new states when the SET CONSTRAINTS ... *
* command finishes and calls EndQuery. * In a scenario with many commands in a transaction and many
* deferred-to-end-of-transaction triggers, it could get annoying
* to rescan all the deferred triggers at each command end.
* To speed this up, we could remember the actual end of the queue at
* EndQuery and examine only events that are newer. On state changes
* we simply reset the saved position to the beginning of the queue
* and process all events once with the new states.
*/ */
/* Make a per-tuple memory context for trigger function calls */ /* Make a per-tuple memory context for trigger function calls */
...@@ -1495,24 +1456,24 @@ deferredTriggerInvokeEvents(bool immediate_only) ...@@ -1495,24 +1456,24 @@ deferredTriggerInvokeEvents(bool immediate_only)
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE); ALLOCSET_DEFAULT_MAXSIZE);
for (event = deftrig_events; event != NULL; event = event->dte_next) event = deftrig_events;
while (event != NULL)
{ {
bool still_deferred_ones; bool still_deferred_ones = false;
DeferredTriggerEvent next_event;
int i; int i;
/* /*
* Check if event is completely done. * Check if event is already completely done.
*/ */
if (event->dte_event & (TRIGGER_DEFERRED_DONE | if (! (event->dte_event & (TRIGGER_DEFERRED_DONE |
TRIGGER_DEFERRED_CANCELED)) TRIGGER_DEFERRED_CANCELED)))
continue; {
MemoryContextReset(per_tuple_context); MemoryContextReset(per_tuple_context);
/* /*
* Check each trigger item in the event. * Check each trigger item in the event.
*/ */
still_deferred_ones = false;
for (i = 0; i < event->dte_n_items; i++) for (i = 0; i < event->dte_n_items; i++)
{ {
if (event->dte_item[i].dti_state & TRIGGER_DEFERRED_DONE) if (event->dte_item[i].dti_state & TRIGGER_DEFERRED_DONE)
...@@ -1522,8 +1483,8 @@ deferredTriggerInvokeEvents(bool immediate_only) ...@@ -1522,8 +1483,8 @@ deferredTriggerInvokeEvents(bool immediate_only)
* This trigger item hasn't been called yet. Check if we * This trigger item hasn't been called yet. Check if we
* should call it now. * should call it now.
*/ */
if (immediate_only && deferredTriggerCheckState( if (immediate_only &&
event->dte_item[i].dti_tgoid, deferredTriggerCheckState(event->dte_item[i].dti_tgoid,
event->dte_item[i].dti_state)) event->dte_item[i].dti_state))
{ {
still_deferred_ones = true; still_deferred_ones = true;
...@@ -1531,8 +1492,8 @@ deferredTriggerInvokeEvents(bool immediate_only) ...@@ -1531,8 +1492,8 @@ deferredTriggerInvokeEvents(bool immediate_only)
} }
/* /*
* So let's fire it... but first, open the correct relation if * So let's fire it... but first, open the correct relation
* this is not the same relation as before. * if this is not the same relation as before.
*/ */
if (rel == NULL || rel->rd_id != event->dte_relid) if (rel == NULL || rel->rd_id != event->dte_relid)
{ {
...@@ -1557,18 +1518,56 @@ deferredTriggerInvokeEvents(bool immediate_only) ...@@ -1557,18 +1518,56 @@ deferredTriggerInvokeEvents(bool immediate_only)
rel->trigdesc->numtriggers * sizeof(FmgrInfo)); rel->trigdesc->numtriggers * sizeof(FmgrInfo));
} }
DeferredTriggerExecute(event, i, rel, finfo, per_tuple_context); DeferredTriggerExecute(event, i, rel, finfo,
per_tuple_context);
event->dte_item[i].dti_state |= TRIGGER_DEFERRED_DONE; event->dte_item[i].dti_state |= TRIGGER_DEFERRED_DONE;
} /* end loop over items within event */
} }
/* /*
* Remember in the event itself if all trigger items are done. * If it's now completely done, throw it away.
*
* NB: it's possible the trigger calls above added more events to the
* queue, or that calls we will do later will want to add more,
* so we have to be careful about maintaining list validity here.
*/
next_event = event->dte_next;
if (still_deferred_ones)
{
/* Not done, keep in list */
prev_event = event;
}
else
{
/* Done */
if (immediate_only)
{
/* delink it from list and free it */
if (prev_event)
prev_event->dte_next = next_event;
else
deftrig_events = next_event;
pfree(event);
}
else
{
/*
* We will clean up later, but just for paranoia's sake,
* mark the event done.
*/ */
if (!still_deferred_ones)
event->dte_event |= TRIGGER_DEFERRED_DONE; event->dte_event |= TRIGGER_DEFERRED_DONE;
} }
}
event = next_event;
}
/* Update list tail pointer in case we just deleted tail event */
deftrig_event_tail = prev_event;
/* Release working resources */
if (rel) if (rel)
heap_close(rel, NoLock); heap_close(rel, NoLock);
if (finfo) if (finfo)
...@@ -1649,7 +1648,6 @@ DeferredTriggerBeginXact(void) ...@@ -1649,7 +1648,6 @@ DeferredTriggerBeginXact(void)
MemoryContextSwitchTo(oldcxt); MemoryContextSwitchTo(oldcxt);
deftrig_n_events = 0;
deftrig_events = NULL; deftrig_events = NULL;
deftrig_event_tail = NULL; deftrig_event_tail = NULL;
} }
...@@ -1986,9 +1984,7 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt) ...@@ -1986,9 +1984,7 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt)
* Called by ExecAR...Triggers() to add the event to the queue. * Called by ExecAR...Triggers() to add the event to the queue.
* *
* NOTE: should be called only if we've determined that an event must * NOTE: should be called only if we've determined that an event must
* be added to the queue. We must save *all* events if there is either * be added to the queue.
* an UPDATE or a DELETE deferred trigger; see uses of
* deferredTriggerGetPreviousEvent.
* ---------- * ----------
*/ */
static void static void
...@@ -1999,7 +1995,6 @@ DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event, ...@@ -1999,7 +1995,6 @@ DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event,
TriggerDesc *trigdesc = relinfo->ri_TrigDesc; TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
MemoryContext oldcxt; MemoryContext oldcxt;
DeferredTriggerEvent new_event; DeferredTriggerEvent new_event;
DeferredTriggerEvent prev_event;
int new_size; int new_size;
int i; int i;
int ntriggers; int ntriggers;
...@@ -2060,26 +2055,12 @@ DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event, ...@@ -2060,26 +2055,12 @@ DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event,
switch (event & TRIGGER_EVENT_OPMASK) switch (event & TRIGGER_EVENT_OPMASK)
{ {
case TRIGGER_EVENT_INSERT: case TRIGGER_EVENT_INSERT:
new_event->dte_event |= TRIGGER_DEFERRED_ROW_INSERTED; /* nothing to do */
new_event->dte_event |= TRIGGER_DEFERRED_KEY_CHANGED;
break; break;
case TRIGGER_EVENT_UPDATE: 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 (!TransactionIdEquals(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. * Check if one of the referenced keys is changed.
*/ */
for (i = 0; i < ntriggers; i++) for (i = 0; i < ntriggers; i++)
{ {
...@@ -2120,109 +2101,21 @@ DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event, ...@@ -2120,109 +2101,21 @@ DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event,
{ {
/* /*
* The key hasn't changed, so no need later to invoke * The key hasn't changed, so no need later to invoke
* the trigger at all. But remember other states from * the trigger at all.
* the possible earlier event.
*/ */
new_event->dte_item[i].dti_state |= TRIGGER_DEFERRED_DONE; 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\"",
DatumGetCString(DirectFunctionCall1(nameout,
NameGetDatum(&(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\"",
DatumGetCString(DirectFunctionCall1(nameout,
NameGetDatum(&(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; break;
case TRIGGER_EVENT_DELETE: case TRIGGER_EVENT_DELETE:
/* nothing to do */
/*
* On DELETE check if the tuple deleted has been inserted or a
* possibly referenced key value has changed in this
* transaction.
*/
if (!TransactionIdEquals(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\"",
DatumGetCString(DirectFunctionCall1(nameout,
NameGetDatum(&(rel->rd_rel->relname)))));
break; break;
} }
/* /*
* Anything's fine up to here. Add the new event to the queue. * Add the new event to the queue.
*/ */
oldcxt = MemoryContextSwitchTo(deftrig_cxt);
deferredTriggerAddEvent(new_event); deferredTriggerAddEvent(new_event);
MemoryContextSwitchTo(oldcxt);
} }
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: trigger.h,v 1.31 2001/11/12 00:46:36 tgl Exp $ * $Id: trigger.h,v 1.32 2001/11/16 16:31:16 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -50,9 +50,6 @@ typedef struct TriggerData ...@@ -50,9 +50,6 @@ typedef struct TriggerData
#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_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) == \
......
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