Remove 'triggered data change violation' error check, per recent
discussions in pghackers.
This commit is contained in:
parent
306798ded5
commit
7c50767f08
@ -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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
{
|
{
|
||||||
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;
|
|||||||
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)
|
|||||||
* 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)
|
|||||||
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,
|
|||||||
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,80 +1456,118 @@ 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);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check each trigger item in the event.
|
|
||||||
*/
|
|
||||||
still_deferred_ones = false;
|
|
||||||
for (i = 0; i < event->dte_n_items; i++)
|
|
||||||
{
|
{
|
||||||
if (event->dte_item[i].dti_state & TRIGGER_DEFERRED_DONE)
|
MemoryContextReset(per_tuple_context);
|
||||||
continue;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This trigger item hasn't been called yet. Check if we
|
* Check each trigger item in the event.
|
||||||
* should call it now.
|
|
||||||
*/
|
*/
|
||||||
if (immediate_only && deferredTriggerCheckState(
|
for (i = 0; i < event->dte_n_items; i++)
|
||||||
event->dte_item[i].dti_tgoid,
|
|
||||||
event->dte_item[i].dti_state))
|
|
||||||
{
|
{
|
||||||
still_deferred_ones = true;
|
if (event->dte_item[i].dti_state & TRIGGER_DEFERRED_DONE)
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* So let's fire it... but first, open the correct relation if
|
|
||||||
* this is not the same relation as before.
|
|
||||||
*/
|
|
||||||
if (rel == NULL || rel->rd_id != event->dte_relid)
|
|
||||||
{
|
|
||||||
if (rel)
|
|
||||||
heap_close(rel, NoLock);
|
|
||||||
if (finfo)
|
|
||||||
pfree(finfo);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We assume that an appropriate lock is still held by the
|
* This trigger item hasn't been called yet. Check if we
|
||||||
* executor, so grab no new lock here.
|
* should call it now.
|
||||||
*/
|
*/
|
||||||
rel = heap_open(event->dte_relid, NoLock);
|
if (immediate_only &&
|
||||||
|
deferredTriggerCheckState(event->dte_item[i].dti_tgoid,
|
||||||
|
event->dte_item[i].dti_state))
|
||||||
|
{
|
||||||
|
still_deferred_ones = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate space to cache fmgr lookup info for triggers
|
* So let's fire it... but first, open the correct relation
|
||||||
* of this relation.
|
* if this is not the same relation as before.
|
||||||
*/
|
*/
|
||||||
finfo = (FmgrInfo *)
|
if (rel == NULL || rel->rd_id != event->dte_relid)
|
||||||
palloc(rel->trigdesc->numtriggers * sizeof(FmgrInfo));
|
{
|
||||||
MemSet(finfo, 0,
|
if (rel)
|
||||||
rel->trigdesc->numtriggers * sizeof(FmgrInfo));
|
heap_close(rel, NoLock);
|
||||||
}
|
if (finfo)
|
||||||
|
pfree(finfo);
|
||||||
|
|
||||||
DeferredTriggerExecute(event, i, rel, finfo, per_tuple_context);
|
/*
|
||||||
|
* We assume that an appropriate lock is still held by the
|
||||||
|
* executor, so grab no new lock here.
|
||||||
|
*/
|
||||||
|
rel = heap_open(event->dte_relid, NoLock);
|
||||||
|
|
||||||
event->dte_item[i].dti_state |= TRIGGER_DEFERRED_DONE;
|
/*
|
||||||
|
* Allocate space to cache fmgr lookup info for triggers
|
||||||
|
* of this relation.
|
||||||
|
*/
|
||||||
|
finfo = (FmgrInfo *)
|
||||||
|
palloc(rel->trigdesc->numtriggers * sizeof(FmgrInfo));
|
||||||
|
MemSet(finfo, 0,
|
||||||
|
rel->trigdesc->numtriggers * sizeof(FmgrInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
DeferredTriggerExecute(event, i, rel, finfo,
|
||||||
|
per_tuple_context);
|
||||||
|
|
||||||
|
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.
|
||||||
*/
|
*/
|
||||||
if (!still_deferred_ones)
|
next_event = event->dte_next;
|
||||||
event->dte_event |= TRIGGER_DEFERRED_DONE;
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
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)
|
|||||||
|
|
||||||
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)
|
|||||||
* 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,
|
|||||||
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,
|
|||||||
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
|
* Check if one of the referenced keys is changed.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < ntriggers; i++)
|
for (i = 0; i < ntriggers; i++)
|
||||||
{
|
{
|
||||||
@ -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 @@
|
|||||||
* 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
|
|||||||
#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) == \
|
||||||
|
Loading…
x
Reference in New Issue
Block a user