diff options
author | Tom Lane | 2007-08-15 21:39:50 +0000 |
---|---|---|
committer | Tom Lane | 2007-08-15 21:39:50 +0000 |
commit | 817946bb04e1dcac02d85572103f1e1381102a0a (patch) | |
tree | 221b38d8cae6163c5d4f9e9248e1555f9042e685 /src/backend/commands/trigger.c | |
parent | 9cb84097623e5efe32b7289ca3c403f70ee152d8 (diff) |
Arrange to cache a ResultRelInfo in the executor's EState for relations that
are not one of the query's defined result relations, but nonetheless have
triggers fired against them while the query is active. This was formerly
impossible but can now occur because of my recent patch to fix the firing
order for RI triggers. Caching a ResultRelInfo avoids duplicating work by
repeatedly opening and closing the same relation, and also allows EXPLAIN
ANALYZE to "see" and report on these extra triggers. Use the same mechanism
to cache open relations when firing deferred triggers at transaction shutdown;
this replaces the former one-element-cache strategy used in that case, and
should improve performance a bit when there are deferred triggers on a number
of relations.
Diffstat (limited to 'src/backend/commands/trigger.c')
-rw-r--r-- | src/backend/commands/trigger.c | 123 |
1 files changed, 39 insertions, 84 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 2788684d1ab..afcdaa5e913 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.217 2007/08/15 19:15:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.218 2007/08/15 21:39:50 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2313,11 +2313,10 @@ afterTriggerMarkEvents(AfterTriggerEventList *events, * Scan the given event list for events that are marked as to be fired * in the current firing cycle, and fire them. * - * If estate isn't NULL, then we expect that all the firable events are - * for triggers of the relations included in the estate's result relation - * array. This allows us to re-use the estate's open relations and - * trigger cache info. When estate is NULL, we have to find the relations - * the hard way. + * If estate isn't NULL, we use its result relation info to avoid repeated + * openings and closing of trigger target relations. If it is NULL, we + * make one locally to cache the info in case there are multiple trigger + * events per rel. * * When delete_ok is TRUE, it's okay to delete fully-processed events. * The events list pointers are updated. @@ -2332,12 +2331,19 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events, AfterTriggerEvent event, prev_event; MemoryContext per_tuple_context; - bool locally_opened = false; + bool local_estate = false; Relation rel = NULL; TriggerDesc *trigdesc = NULL; FmgrInfo *finfo = NULL; Instrumentation *instr = NULL; + /* Make a local EState if need be */ + if (estate == NULL) + { + estate = CreateExecutorState(); + local_estate = true; + } + /* Make a per-tuple memory context for trigger function calls */ per_tuple_context = AllocSetContextCreate(CurrentMemoryContext, @@ -2360,77 +2366,21 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events, event->ate_firing_id == firing_id) { /* - * So let's fire it... but first, open the correct relation if + * So let's fire it... but first, find the correct relation if * this is not the same relation as before. */ - if (rel == NULL || rel->rd_id != event->ate_relid) + if (rel == NULL || RelationGetRelid(rel) != event->ate_relid) { - if (locally_opened) - { - /* close prior rel if any */ - if (rel) - heap_close(rel, NoLock); - if (trigdesc) - FreeTriggerDesc(trigdesc); - if (finfo) - pfree(finfo); - Assert(instr == NULL); /* never used in this case */ - } - locally_opened = true; - - if (estate) - { - /* Find target relation among estate's result rels */ - ResultRelInfo *rInfo; - int nr; - - rInfo = estate->es_result_relations; - nr = estate->es_num_result_relations; - while (nr > 0) - { - if (rInfo->ri_RelationDesc->rd_id == event->ate_relid) - { - rel = rInfo->ri_RelationDesc; - trigdesc = rInfo->ri_TrigDesc; - finfo = rInfo->ri_TrigFunctions; - instr = rInfo->ri_TrigInstrument; - locally_opened = false; - break; - } - rInfo++; - nr--; - } - } - - if (locally_opened) - { - /* Hard way: open target relation for ourselves */ - - /* - * We assume that an appropriate lock is still held by the - * executor, so grab no new lock here. - */ - rel = heap_open(event->ate_relid, NoLock); - - /* - * Copy relation's trigger info so that we have a stable - * copy no matter what the called triggers do. - */ - trigdesc = CopyTriggerDesc(rel->trigdesc); - - if (trigdesc == NULL) /* should not happen */ - elog(ERROR, "relation %u has no triggers", - event->ate_relid); - - /* - * Allocate space to cache fmgr lookup info for triggers. - */ - finfo = (FmgrInfo *) - palloc0(trigdesc->numtriggers * sizeof(FmgrInfo)); - - /* Never any EXPLAIN info in this case */ - instr = NULL; - } + ResultRelInfo *rInfo; + + rInfo = ExecGetTriggerResultRel(estate, event->ate_relid); + rel = rInfo->ri_RelationDesc; + trigdesc = rInfo->ri_TrigDesc; + finfo = rInfo->ri_TrigFunctions; + instr = rInfo->ri_TrigInstrument; + if (trigdesc == NULL) /* should not happen */ + elog(ERROR, "relation %u has no triggers", + event->ate_relid); } /* @@ -2480,17 +2430,22 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events, events->tail = prev_event; /* Release working resources */ - if (locally_opened) + MemoryContextDelete(per_tuple_context); + + if (local_estate) { - if (rel) - heap_close(rel, NoLock); - if (trigdesc) - FreeTriggerDesc(trigdesc); - if (finfo) - pfree(finfo); - Assert(instr == NULL); /* never used in this case */ + ListCell *l; + + foreach(l, estate->es_trig_target_relations) + { + ResultRelInfo *resultRelInfo = (ResultRelInfo *) lfirst(l); + + /* Close indices and then the relation itself */ + ExecCloseIndices(resultRelInfo); + heap_close(resultRelInfo->ri_RelationDesc, NoLock); + } + FreeExecutorState(estate); } - MemoryContextDelete(per_tuple_context); } |