mirror of
https://github.com/postgres/postgres.git
synced 2025-10-28 11:55:03 +03:00
Back-patch change to avoid O(N^2) behavior with lots of deferred triggers,
by making deferredTriggerInvokeEvents only scan events added since it last ran.
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.136.2.1 2003/03/27 14:33:20 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.136.2.2 2003/05/19 17:23:54 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1461,12 +1461,18 @@ static List *deftrig_trigstates;
|
||||
* Because this can grow pretty large, we don't use separate List nodes,
|
||||
* 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.
|
||||
*
|
||||
* deftrig_events_imm holds the tail pointer as of the last
|
||||
* deferredTriggerInvokeEvents call; we can use this to avoid rescanning
|
||||
* entries unnecessarily. It is NULL if deferredTriggerInvokeEvents
|
||||
* hasn't run since the last state change.
|
||||
*
|
||||
* XXX Need to be able to shove this data out to a file if it grows too
|
||||
* large...
|
||||
* ----------
|
||||
*/
|
||||
static DeferredTriggerEvent deftrig_events;
|
||||
static DeferredTriggerEvent deftrig_events_imm;
|
||||
static DeferredTriggerEvent deftrig_event_tail;
|
||||
|
||||
|
||||
@@ -1680,7 +1686,7 @@ static void
|
||||
deferredTriggerInvokeEvents(bool immediate_only)
|
||||
{
|
||||
DeferredTriggerEvent event,
|
||||
prev_event = NULL;
|
||||
prev_event;
|
||||
MemoryContext per_tuple_context;
|
||||
Relation rel = NULL;
|
||||
TriggerDesc *trigdesc = NULL;
|
||||
@@ -1692,13 +1698,12 @@ deferredTriggerInvokeEvents(bool immediate_only)
|
||||
* are going to discard the whole event queue on return anyway, so no
|
||||
* need to bother with "retail" pfree's.
|
||||
*
|
||||
* 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.
|
||||
* If immediate_only is true, we need only scan from where the end of
|
||||
* the queue was at the previous deferredTriggerInvokeEvents call;
|
||||
* any non-deferred events before that point are already fired.
|
||||
* (But if the deferral state changes, we must reset the saved position
|
||||
* to the beginning of the queue, so as to process all events once with
|
||||
* the new states. See DeferredTriggerSetState.)
|
||||
*/
|
||||
|
||||
/* Make a per-tuple memory context for trigger function calls */
|
||||
@@ -1709,7 +1714,22 @@ deferredTriggerInvokeEvents(bool immediate_only)
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
|
||||
event = deftrig_events;
|
||||
/*
|
||||
* If immediate_only is true, then the only events that could need firing
|
||||
* are those since deftrig_events_imm. (But if deftrig_events_imm is
|
||||
* NULL, we must scan the entire list.)
|
||||
*/
|
||||
if (immediate_only && deftrig_events_imm != NULL)
|
||||
{
|
||||
prev_event = deftrig_events_imm;
|
||||
event = prev_event->dte_next;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_event = NULL;
|
||||
event = deftrig_events;
|
||||
}
|
||||
|
||||
while (event != NULL)
|
||||
{
|
||||
bool still_deferred_ones = false;
|
||||
@@ -1830,6 +1850,9 @@ deferredTriggerInvokeEvents(bool immediate_only)
|
||||
/* Update list tail pointer in case we just deleted tail event */
|
||||
deftrig_event_tail = prev_event;
|
||||
|
||||
/* Set the immediate event pointer for next time */
|
||||
deftrig_events_imm = prev_event;
|
||||
|
||||
/* Release working resources */
|
||||
if (rel)
|
||||
heap_close(rel, NoLock);
|
||||
@@ -1917,6 +1940,7 @@ DeferredTriggerBeginXact(void)
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
deftrig_events = NULL;
|
||||
deftrig_events_imm = NULL;
|
||||
deftrig_event_tail = NULL;
|
||||
}
|
||||
|
||||
@@ -2146,8 +2170,11 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt)
|
||||
* CONSTRAINTS command applies retroactively. This happens "for free"
|
||||
* since we have already made the necessary modifications to the
|
||||
* constraints, and deferredTriggerEndQuery() is called by
|
||||
* finish_xact_command().
|
||||
* finish_xact_command(). But we must reset deferredTriggerInvokeEvents'
|
||||
* tail pointer to make it rescan the entire list, in case some deferred
|
||||
* events are now immediately invokable.
|
||||
*/
|
||||
deftrig_events_imm = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user