Skip to content

Commit 817946b

Browse files
committed
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.
1 parent 9cb8409 commit 817946b

File tree

6 files changed

+223
-139
lines changed

6 files changed

+223
-139
lines changed

src/backend/commands/explain.c

Lines changed: 57 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1994-5, Regents of the University of California
88
*
99
* IDENTIFICATION
10-
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.164 2007/05/25 17:54:24 tgl Exp $
10+
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.165 2007/08/15 21:39:50 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -52,6 +52,8 @@ typedef struct ExplainState
5252
static void ExplainOneQuery(Query *query, ExplainStmt *stmt,
5353
const char *queryString,
5454
ParamListInfo params, TupOutputState *tstate);
55+
static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
56+
StringInfo buf);
5557
static double elapsed_time(instr_time *starttime);
5658
static void explain_outNode(StringInfo str,
5759
Plan *plan, PlanState *planstate,
@@ -310,50 +312,21 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ParamListInfo params,
310312
if (es->printAnalyze)
311313
{
312314
ResultRelInfo *rInfo;
315+
bool show_relname;
313316
int numrels = queryDesc->estate->es_num_result_relations;
317+
List *targrels = queryDesc->estate->es_trig_target_relations;
314318
int nr;
319+
ListCell *l;
315320

321+
show_relname = (numrels > 1 || targrels != NIL);
316322
rInfo = queryDesc->estate->es_result_relations;
317323
for (nr = 0; nr < numrels; rInfo++, nr++)
318-
{
319-
int nt;
320-
321-
if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
322-
continue;
323-
for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
324-
{
325-
Trigger *trig = rInfo->ri_TrigDesc->triggers + nt;
326-
Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
327-
char *conname;
324+
report_triggers(rInfo, show_relname, &buf);
328325

329-
/* Must clean up instrumentation state */
330-
InstrEndLoop(instr);
331-
332-
/*
333-
* We ignore triggers that were never invoked; they likely
334-
* aren't relevant to the current query type.
335-
*/
336-
if (instr->ntuples == 0)
337-
continue;
338-
339-
if (OidIsValid(trig->tgconstraint) &&
340-
(conname = get_constraint_name(trig->tgconstraint)) != NULL)
341-
{
342-
appendStringInfo(&buf, "Trigger for constraint %s",
343-
conname);
344-
pfree(conname);
345-
}
346-
else
347-
appendStringInfo(&buf, "Trigger %s", trig->tgname);
348-
349-
if (numrels > 1)
350-
appendStringInfo(&buf, " on %s",
351-
RelationGetRelationName(rInfo->ri_RelationDesc));
352-
353-
appendStringInfo(&buf, ": time=%.3f calls=%.0f\n",
354-
1000.0 * instr->total,
355-
instr->ntuples);
356-
}
326+
foreach(l, targrels)
327+
{
328+
rInfo = (ResultRelInfo *) lfirst(l);
329+
report_triggers(rInfo, show_relname, &buf);
357330
}
358331
}
359332

@@ -382,6 +355,51 @@ ExplainOnePlan(PlannedStmt *plannedstmt, ParamListInfo params,
382355
pfree(es);
383356
}
384357

358+
/*
359+
* report_triggers -
360+
* report execution stats for a single relation's triggers
361+
*/
362+
static void
363+
report_triggers(ResultRelInfo *rInfo, bool show_relname, StringInfo buf)
364+
{
365+
int nt;
366+
367+
if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
368+
return;
369+
for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
370+
{
371+
Trigger *trig = rInfo->ri_TrigDesc->triggers + nt;
372+
Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
373+
char *conname;
374+
375+
/* Must clean up instrumentation state */
376+
InstrEndLoop(instr);
377+
378+
/*
379+
* We ignore triggers that were never invoked; they likely
380+
* aren't relevant to the current query type.
381+
*/
382+
if (instr->ntuples == 0)
383+
continue;
384+
385+
if (OidIsValid(trig->tgconstraint) &&
386+
(conname = get_constraint_name(trig->tgconstraint)) != NULL)
387+
{
388+
appendStringInfo(buf, "Trigger for constraint %s", conname);
389+
pfree(conname);
390+
}
391+
else
392+
appendStringInfo(buf, "Trigger %s", trig->tgname);
393+
394+
if (show_relname)
395+
appendStringInfo(buf, " on %s",
396+
RelationGetRelationName(rInfo->ri_RelationDesc));
397+
398+
appendStringInfo(buf, ": time=%.3f calls=%.0f\n",
399+
1000.0 * instr->total, instr->ntuples);
400+
}
401+
}
402+
385403
/* Compute elapsed time in seconds since given timestamp */
386404
static double
387405
elapsed_time(instr_time *starttime)

src/backend/commands/trigger.c

Lines changed: 39 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1994, Regents of the University of California
88
*
99
* IDENTIFICATION
10-
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.217 2007/08/15 19:15:46 tgl Exp $
10+
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.218 2007/08/15 21:39:50 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -2313,11 +2313,10 @@ afterTriggerMarkEvents(AfterTriggerEventList *events,
23132313
* Scan the given event list for events that are marked as to be fired
23142314
* in the current firing cycle, and fire them.
23152315
*
2316-
* If estate isn't NULL, then we expect that all the firable events are
2317-
* for triggers of the relations included in the estate's result relation
2318-
* array. This allows us to re-use the estate's open relations and
2319-
* trigger cache info. When estate is NULL, we have to find the relations
2320-
* the hard way.
2316+
* If estate isn't NULL, we use its result relation info to avoid repeated
2317+
* openings and closing of trigger target relations. If it is NULL, we
2318+
* make one locally to cache the info in case there are multiple trigger
2319+
* events per rel.
23212320
*
23222321
* When delete_ok is TRUE, it's okay to delete fully-processed events.
23232322
* The events list pointers are updated.
@@ -2332,12 +2331,19 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
23322331
AfterTriggerEvent event,
23332332
prev_event;
23342333
MemoryContext per_tuple_context;
2335-
bool locally_opened = false;
2334+
bool local_estate = false;
23362335
Relation rel = NULL;
23372336
TriggerDesc *trigdesc = NULL;
23382337
FmgrInfo *finfo = NULL;
23392338
Instrumentation *instr = NULL;
23402339

2340+
/* Make a local EState if need be */
2341+
if (estate == NULL)
2342+
{
2343+
estate = CreateExecutorState();
2344+
local_estate = true;
2345+
}
2346+
23412347
/* Make a per-tuple memory context for trigger function calls */
23422348
per_tuple_context =
23432349
AllocSetContextCreate(CurrentMemoryContext,
@@ -2360,77 +2366,21 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
23602366
event->ate_firing_id == firing_id)
23612367
{
23622368
/*
2363-
* So let's fire it... but first, open the correct relation if
2369+
* So let's fire it... but first, find the correct relation if
23642370
* this is not the same relation as before.
23652371
*/
2366-
if (rel == NULL || rel->rd_id != event->ate_relid)
2372+
if (rel == NULL || RelationGetRelid(rel) != event->ate_relid)
23672373
{
2368-
if (locally_opened)
2369-
{
2370-
/* close prior rel if any */
2371-
if (rel)
2372-
heap_close(rel, NoLock);
2373-
if (trigdesc)
2374-
FreeTriggerDesc(trigdesc);
2375-
if (finfo)
2376-
pfree(finfo);
2377-
Assert(instr == NULL); /* never used in this case */
2378-
}
2379-
locally_opened = true;
2380-
2381-
if (estate)
2382-
{
2383-
/* Find target relation among estate's result rels */
2384-
ResultRelInfo *rInfo;
2385-
int nr;
2386-
2387-
rInfo = estate->es_result_relations;
2388-
nr = estate->es_num_result_relations;
2389-
while (nr > 0)
2390-
{
2391-
if (rInfo->ri_RelationDesc->rd_id == event->ate_relid)
2392-
{
2393-
rel = rInfo->ri_RelationDesc;
2394-
trigdesc = rInfo->ri_TrigDesc;
2395-
finfo = rInfo->ri_TrigFunctions;
2396-
instr = rInfo->ri_TrigInstrument;
2397-
locally_opened = false;
2398-
break;
2399-
}
2400-
rInfo++;
2401-
nr--;
2402-
}
2403-
}
2404-
2405-
if (locally_opened)
2406-
{
2407-
/* Hard way: open target relation for ourselves */
2408-
2409-
/*
2410-
* We assume that an appropriate lock is still held by the
2411-
* executor, so grab no new lock here.
2412-
*/
2413-
rel = heap_open(event->ate_relid, NoLock);
2414-
2415-
/*
2416-
* Copy relation's trigger info so that we have a stable
2417-
* copy no matter what the called triggers do.
2418-
*/
2419-
trigdesc = CopyTriggerDesc(rel->trigdesc);
2420-
2421-
if (trigdesc == NULL) /* should not happen */
2422-
elog(ERROR, "relation %u has no triggers",
2423-
event->ate_relid);
2424-
2425-
/*
2426-
* Allocate space to cache fmgr lookup info for triggers.
2427-
*/
2428-
finfo = (FmgrInfo *)
2429-
palloc0(trigdesc->numtriggers * sizeof(FmgrInfo));
2430-
2431-
/* Never any EXPLAIN info in this case */
2432-
instr = NULL;
2433-
}
2374+
ResultRelInfo *rInfo;
2375+
2376+
rInfo = ExecGetTriggerResultRel(estate, event->ate_relid);
2377+
rel = rInfo->ri_RelationDesc;
2378+
trigdesc = rInfo->ri_TrigDesc;
2379+
finfo = rInfo->ri_TrigFunctions;
2380+
instr = rInfo->ri_TrigInstrument;
2381+
if (trigdesc == NULL) /* should not happen */
2382+
elog(ERROR, "relation %u has no triggers",
2383+
event->ate_relid);
24342384
}
24352385

24362386
/*
@@ -2480,17 +2430,22 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
24802430
events->tail = prev_event;
24812431

24822432
/* Release working resources */
2483-
if (locally_opened)
2433+
MemoryContextDelete(per_tuple_context);
2434+
2435+
if (local_estate)
24842436
{
2485-
if (rel)
2486-
heap_close(rel, NoLock);
2487-
if (trigdesc)
2488-
FreeTriggerDesc(trigdesc);
2489-
if (finfo)
2490-
pfree(finfo);
2491-
Assert(instr == NULL); /* never used in this case */
2437+
ListCell *l;
2438+
2439+
foreach(l, estate->es_trig_target_relations)
2440+
{
2441+
ResultRelInfo *resultRelInfo = (ResultRelInfo *) lfirst(l);
2442+
2443+
/* Close indices and then the relation itself */
2444+
ExecCloseIndices(resultRelInfo);
2445+
heap_close(resultRelInfo->ri_RelationDesc, NoLock);
2446+
}
2447+
FreeExecutorState(estate);
24922448
}
2493-
MemoryContextDelete(per_tuple_context);
24942449
}
24952450

24962451

0 commit comments

Comments
 (0)