Improve EXPLAIN ANALYZE to show the time spent in each trigger when

executing a statement that fires triggers.  Formerly this time was
included in "Total runtime" but not otherwise accounted for.
As a side benefit, we avoid re-opening relations when firing non-deferred
AFTER triggers, because the trigger code can re-use the main executor's
ResultRelInfo data structure.
This commit is contained in:
Tom Lane 2005-03-25 21:58:00 +00:00
parent 08890b407e
commit adb1a6e95b
16 changed files with 403 additions and 143 deletions

View File

@ -1,5 +1,5 @@
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/perform.sgml,v 1.50 2005/02/03 07:12:37 neilc Exp $ $PostgreSQL: pgsql/doc/src/sgml/perform.sgml,v 1.51 2005/03/25 21:57:57 tgl Exp $
--> -->
<chapter id="performance-tips"> <chapter id="performance-tips">
@ -320,16 +320,19 @@ EXPLAIN ANALYZE SELECT * FROM tenk1 t1, tenk2 t2 WHERE t1.unique1 &lt; 50 AND t1
</para> </para>
<para> <para>
The <literal>Total runtime</literal> shown by <command>EXPLAIN ANALYZE</command> includes The <literal>Total runtime</literal> shown by <command>EXPLAIN
executor start-up and shut-down time, as well as time spent processing ANALYZE</command> includes executor start-up and shut-down time, as well
the result rows. It does not include parsing, rewriting, or planning as time spent processing the result rows. It does not include parsing,
time. For a <command>SELECT</> query, the total run time will normally be just a rewriting, or planning time. For a <command>SELECT</> query, the total
little larger than the total time reported for the top-level plan node. run time will normally be just a little larger than the total time
For <command>INSERT</>, <command>UPDATE</>, and <command>DELETE</> commands, the total run time may be reported for the top-level plan node. For <command>INSERT</>,
considerably larger, because it includes the time spent processing the <command>UPDATE</>, and <command>DELETE</> commands, the total run time
result rows. In these commands, the time for the top plan node may be considerably larger, because it includes the time spent processing
essentially is the time spent computing the new rows and/or locating the result rows. In these commands, the time for the top plan node
the old ones, but it doesn't include the time spent making the changes. essentially is the time spent computing the new rows and/or locating the
old ones, but it doesn't include the time spent making the changes.
Time spent firing triggers, if any, is also outside the top plan node,
and is shown separately for each trigger.
</para> </para>
<para> <para>

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.22 2004/12/31 21:59:38 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.23 2005/03/25 21:57:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -21,12 +21,14 @@
#include "catalog/dependency.h" #include "catalog/dependency.h"
#include "catalog/indexing.h" #include "catalog/indexing.h"
#include "catalog/pg_constraint.h" #include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "utils/array.h" #include "utils/array.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/fmgroids.h" #include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h" #include "utils/syscache.h"
@ -510,3 +512,95 @@ RemoveConstraintById(Oid conId)
systable_endscan(conscan); systable_endscan(conscan);
heap_close(conDesc, RowExclusiveLock); heap_close(conDesc, RowExclusiveLock);
} }
/*
* GetConstraintNameForTrigger
* Get the name of the constraint owning a trigger, if any
*
* Returns a palloc'd string, or NULL if no constraint can be found
*/
char *
GetConstraintNameForTrigger(Oid triggerId)
{
char *result;
Oid constraintId = InvalidOid;
Oid pg_trigger_id;
Oid pg_constraint_id;
Relation depRel;
Relation conRel;
ScanKeyData key[2];
SysScanDesc scan;
HeapTuple tup;
pg_trigger_id = get_system_catalog_relid(TriggerRelationName);
pg_constraint_id = get_system_catalog_relid(ConstraintRelationName);
/*
* We must grovel through pg_depend to find the owning constraint.
* Perhaps pg_trigger should have a column for the owning constraint ...
* but right now this is not performance-critical code.
*/
depRel = heap_openr(DependRelationName, AccessShareLock);
ScanKeyInit(&key[0],
Anum_pg_depend_classid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(pg_trigger_id));
ScanKeyInit(&key[1],
Anum_pg_depend_objid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(triggerId));
/* assume we can ignore objsubid for a trigger */
scan = systable_beginscan(depRel, DependDependerIndex, true,
SnapshotNow, 2, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
if (foundDep->refclassid == pg_constraint_id &&
foundDep->deptype == DEPENDENCY_INTERNAL)
{
constraintId = foundDep->refobjid;
break;
}
}
systable_endscan(scan);
heap_close(depRel, AccessShareLock);
if (!OidIsValid(constraintId))
return NULL; /* no owning constraint found */
conRel = heap_openr(ConstraintRelationName, AccessShareLock);
ScanKeyInit(&key[0],
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(constraintId));
scan = systable_beginscan(conRel, ConstraintOidIndex, true,
SnapshotNow, 1, key);
tup = systable_getnext(scan);
if (HeapTupleIsValid(tup))
{
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
result = pstrdup(NameStr(con->conname));
}
else
{
/* This arguably should be an error, but we'll just return NULL */
result = NULL;
}
systable_endscan(scan);
heap_close(conRel, AccessShareLock);
return result;
}

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.238 2005/03/16 21:38:05 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.239 2005/03/25 21:57:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -1510,6 +1510,10 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
resultRelInfo->ri_RangeTableIndex = 1; /* dummy */ resultRelInfo->ri_RangeTableIndex = 1; /* dummy */
resultRelInfo->ri_RelationDesc = rel; resultRelInfo->ri_RelationDesc = rel;
resultRelInfo->ri_TrigDesc = CopyTriggerDesc(rel->trigdesc); resultRelInfo->ri_TrigDesc = CopyTriggerDesc(rel->trigdesc);
if (resultRelInfo->ri_TrigDesc)
resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
palloc0(resultRelInfo->ri_TrigDesc->numtriggers * sizeof(FmgrInfo));
resultRelInfo->ri_TrigInstrument = NULL;
ExecOpenIndices(resultRelInfo); ExecOpenIndices(resultRelInfo);
@ -1974,7 +1978,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
/* /*
* Handle queued AFTER triggers * Handle queued AFTER triggers
*/ */
AfterTriggerEndQuery(); AfterTriggerEndQuery(estate);
pfree(values); pfree(values);
pfree(nulls); pfree(nulls);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California * Portions Copyright (c) 1994-5, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.130 2005/03/20 22:27:51 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.131 2005/03/25 21:57:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -15,6 +15,7 @@
#include "access/genam.h" #include "access/genam.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/explain.h" #include "commands/explain.h"
#include "commands/prepare.h" #include "commands/prepare.h"
@ -272,16 +273,76 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
} }
/* /*
* Close down the query and free resources; also run any queued * If we ran the command, run any AFTER triggers it queued. (Note this
* AFTER triggers. Include time for this in the total runtime. * will not include DEFERRED triggers; since those don't run until end of
* transaction, we can't measure them.) Include into total runtime.
*/
if (stmt->analyze)
{
INSTR_TIME_SET_CURRENT(starttime);
AfterTriggerEndQuery(queryDesc->estate);
totaltime += elapsed_time(&starttime);
}
/* Print info about runtime of triggers */
if (es->printAnalyze)
{
ResultRelInfo *rInfo;
int numrels = queryDesc->estate->es_num_result_relations;
int nr;
rInfo = queryDesc->estate->es_result_relations;
for (nr = 0; nr < numrels; rInfo++, nr++)
{
int nt;
if (!rInfo->ri_TrigDesc)
continue;
for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
{
Trigger *trig = rInfo->ri_TrigDesc->triggers + nt;
Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
char *conname;
/* Must clean up instrumentation state */
InstrEndLoop(instr);
/*
* We ignore triggers that were never invoked; they likely
* aren't relevant to the current query type.
*/
if (instr->ntuples == 0)
continue;
if (trig->tgisconstraint &&
(conname = GetConstraintNameForTrigger(trig->tgoid)) != NULL)
{
appendStringInfo(str, "Trigger for constraint %s",
conname);
pfree(conname);
}
else
appendStringInfo(str, "Trigger %s", trig->tgname);
if (numrels > 1)
appendStringInfo(str, " on %s",
RelationGetRelationName(rInfo->ri_RelationDesc));
appendStringInfo(str, ": time=%.3f calls=%.0f\n",
1000.0 * instr->total,
instr->ntuples);
}
}
}
/*
* Close down the query and free resources. Include time for this
* in the total runtime (although it should be pretty minimal).
*/ */
INSTR_TIME_SET_CURRENT(starttime); INSTR_TIME_SET_CURRENT(starttime);
ExecutorEnd(queryDesc); ExecutorEnd(queryDesc);
if (stmt->analyze)
AfterTriggerEndQuery();
FreeQueryDesc(queryDesc); FreeQueryDesc(queryDesc);
/* We need a CCI just in case query expanded to multiple plans */ /* We need a CCI just in case query expanded to multiple plans */

View File

@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.38 2004/12/31 21:59:41 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.39 2005/03/25 21:57:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -281,8 +281,8 @@ PortalCleanup(Portal portal)
PG_TRY(); PG_TRY();
{ {
CurrentResourceOwner = portal->resowner; CurrentResourceOwner = portal->resowner;
ExecutorEnd(queryDesc);
/* we do not need AfterTriggerEndQuery() here */ /* we do not need AfterTriggerEndQuery() here */
ExecutorEnd(queryDesc);
} }
PG_CATCH(); PG_CATCH();
{ {
@ -382,8 +382,8 @@ PersistHoldablePortal(Portal portal)
* Now shut down the inner executor. * Now shut down the inner executor.
*/ */
portal->queryDesc = NULL; /* prevent double shutdown */ portal->queryDesc = NULL; /* prevent double shutdown */
ExecutorEnd(queryDesc);
/* we do not need AfterTriggerEndQuery() here */ /* we do not need AfterTriggerEndQuery() here */
ExecutorEnd(queryDesc);
/* /*
* Reset the position in the result set: ideally, this could be * Reset the position in the result set: ideally, this could be

View File

@ -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
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.180 2005/03/24 00:03:26 neilc Exp $ * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.181 2005/03/25 21:57:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -28,6 +28,7 @@
#include "commands/defrem.h" #include "commands/defrem.h"
#include "commands/trigger.h" #include "commands/trigger.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "executor/instrument.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "parser/parse_func.h" #include "parser/parse_func.h"
@ -46,7 +47,9 @@ static HeapTuple GetTupleForTrigger(EState *estate,
CommandId cid, CommandId cid,
TupleTableSlot **newSlot); TupleTableSlot **newSlot);
static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
int tgindx,
FmgrInfo *finfo, FmgrInfo *finfo,
Instrumentation *instr,
MemoryContext per_tuple_context); MemoryContext per_tuple_context);
static void AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, static void AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event,
bool row_trigger, HeapTuple oldtup, HeapTuple newtup); bool row_trigger, HeapTuple oldtup, HeapTuple newtup);
@ -1107,20 +1110,26 @@ equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2)
* Call a trigger function. * Call a trigger function.
* *
* trigdata: trigger descriptor. * trigdata: trigger descriptor.
* finfo: possibly-cached call info for the function. * tgindx: trigger's index in finfo and instr arrays.
* finfo: array of cached trigger function call information.
* instr: optional array of EXPLAIN ANALYZE instrumentation state.
* per_tuple_context: memory context to execute the function in. * per_tuple_context: memory context to execute the function in.
* *
* Returns the tuple (or NULL) as returned by the function. * Returns the tuple (or NULL) as returned by the function.
*/ */
static HeapTuple static HeapTuple
ExecCallTriggerFunc(TriggerData *trigdata, ExecCallTriggerFunc(TriggerData *trigdata,
int tgindx,
FmgrInfo *finfo, FmgrInfo *finfo,
Instrumentation *instr,
MemoryContext per_tuple_context) MemoryContext per_tuple_context)
{ {
FunctionCallInfoData fcinfo; FunctionCallInfoData fcinfo;
Datum result; Datum result;
MemoryContext oldContext; MemoryContext oldContext;
finfo += tgindx;
/* /*
* We cache fmgr lookup info, to avoid making the lookup again on each * We cache fmgr lookup info, to avoid making the lookup again on each
* call. * call.
@ -1130,6 +1139,12 @@ ExecCallTriggerFunc(TriggerData *trigdata,
Assert(finfo->fn_oid == trigdata->tg_trigger->tgfoid); Assert(finfo->fn_oid == trigdata->tg_trigger->tgfoid);
/*
* If doing EXPLAIN ANALYZE, start charging time to this trigger.
*/
if (instr)
InstrStartNode(instr + tgindx);
/* /*
* Do the function evaluation in the per-tuple memory context, so that * Do the function evaluation in the per-tuple memory context, so that
* leaked memory will be reclaimed once per tuple. Note in particular * leaked memory will be reclaimed once per tuple. Note in particular
@ -1160,6 +1175,13 @@ ExecCallTriggerFunc(TriggerData *trigdata,
errmsg("trigger function %u returned null value", errmsg("trigger function %u returned null value",
fcinfo.flinfo->fn_oid))); fcinfo.flinfo->fn_oid)));
/*
* If doing EXPLAIN ANALYZE, stop charging time to this trigger,
* and count one "tuple returned" (really the number of firings).
*/
if (instr)
InstrStopNode(instr + tgindx, true);
return (HeapTuple) DatumGetPointer(result); return (HeapTuple) DatumGetPointer(result);
} }
@ -1183,11 +1205,6 @@ ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
if (ntrigs == 0) if (ntrigs == 0)
return; return;
/* Allocate cache space for fmgr lookup info, if not done yet */
if (relinfo->ri_TrigFunctions == NULL)
relinfo->ri_TrigFunctions = (FmgrInfo *)
palloc0(trigdesc->numtriggers * sizeof(FmgrInfo));
LocTriggerData.type = T_TriggerData; LocTriggerData.type = T_TriggerData;
LocTriggerData.tg_event = TRIGGER_EVENT_INSERT | LocTriggerData.tg_event = TRIGGER_EVENT_INSERT |
TRIGGER_EVENT_BEFORE; TRIGGER_EVENT_BEFORE;
@ -1205,7 +1222,9 @@ ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
continue; continue;
LocTriggerData.tg_trigger = trigger; LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData, newtuple = ExecCallTriggerFunc(&LocTriggerData,
relinfo->ri_TrigFunctions + tgindx[i], tgindx[i],
relinfo->ri_TrigFunctions,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate)); GetPerTupleMemoryContext(estate));
if (newtuple) if (newtuple)
@ -1237,11 +1256,6 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TriggerData LocTriggerData; TriggerData LocTriggerData;
int i; int i;
/* Allocate cache space for fmgr lookup info, if not done yet */
if (relinfo->ri_TrigFunctions == NULL)
relinfo->ri_TrigFunctions = (FmgrInfo *)
palloc0(trigdesc->numtriggers * sizeof(FmgrInfo));
LocTriggerData.type = T_TriggerData; LocTriggerData.type = T_TriggerData;
LocTriggerData.tg_event = TRIGGER_EVENT_INSERT | LocTriggerData.tg_event = TRIGGER_EVENT_INSERT |
TRIGGER_EVENT_ROW | TRIGGER_EVENT_ROW |
@ -1259,7 +1273,9 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_trigger = trigger; LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData, newtuple = ExecCallTriggerFunc(&LocTriggerData,
relinfo->ri_TrigFunctions + tgindx[i], tgindx[i],
relinfo->ri_TrigFunctions,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate)); GetPerTupleMemoryContext(estate));
if (oldtuple != newtuple && oldtuple != trigtuple) if (oldtuple != newtuple && oldtuple != trigtuple)
heap_freetuple(oldtuple); heap_freetuple(oldtuple);
@ -1300,11 +1316,6 @@ ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
if (ntrigs == 0) if (ntrigs == 0)
return; return;
/* Allocate cache space for fmgr lookup info, if not done yet */
if (relinfo->ri_TrigFunctions == NULL)
relinfo->ri_TrigFunctions = (FmgrInfo *)
palloc0(trigdesc->numtriggers * sizeof(FmgrInfo));
LocTriggerData.type = T_TriggerData; LocTriggerData.type = T_TriggerData;
LocTriggerData.tg_event = TRIGGER_EVENT_DELETE | LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
TRIGGER_EVENT_BEFORE; TRIGGER_EVENT_BEFORE;
@ -1322,7 +1333,9 @@ ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
continue; continue;
LocTriggerData.tg_trigger = trigger; LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData, newtuple = ExecCallTriggerFunc(&LocTriggerData,
relinfo->ri_TrigFunctions + tgindx[i], tgindx[i],
relinfo->ri_TrigFunctions,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate)); GetPerTupleMemoryContext(estate));
if (newtuple) if (newtuple)
@ -1360,11 +1373,6 @@ ExecBRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
if (trigtuple == NULL) if (trigtuple == NULL)
return false; return false;
/* Allocate cache space for fmgr lookup info, if not done yet */
if (relinfo->ri_TrigFunctions == NULL)
relinfo->ri_TrigFunctions = (FmgrInfo *)
palloc0(trigdesc->numtriggers * sizeof(FmgrInfo));
LocTriggerData.type = T_TriggerData; LocTriggerData.type = T_TriggerData;
LocTriggerData.tg_event = TRIGGER_EVENT_DELETE | LocTriggerData.tg_event = TRIGGER_EVENT_DELETE |
TRIGGER_EVENT_ROW | TRIGGER_EVENT_ROW |
@ -1382,7 +1390,9 @@ ExecBRDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_trigger = trigger; LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData, newtuple = ExecCallTriggerFunc(&LocTriggerData,
relinfo->ri_TrigFunctions + tgindx[i], tgindx[i],
relinfo->ri_TrigFunctions,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate)); GetPerTupleMemoryContext(estate));
if (newtuple == NULL) if (newtuple == NULL)
break; break;
@ -1433,11 +1443,6 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
if (ntrigs == 0) if (ntrigs == 0)
return; return;
/* Allocate cache space for fmgr lookup info, if not done yet */
if (relinfo->ri_TrigFunctions == NULL)
relinfo->ri_TrigFunctions = (FmgrInfo *)
palloc0(trigdesc->numtriggers * sizeof(FmgrInfo));
LocTriggerData.type = T_TriggerData; LocTriggerData.type = T_TriggerData;
LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE | LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
TRIGGER_EVENT_BEFORE; TRIGGER_EVENT_BEFORE;
@ -1455,7 +1460,9 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
continue; continue;
LocTriggerData.tg_trigger = trigger; LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData, newtuple = ExecCallTriggerFunc(&LocTriggerData,
relinfo->ri_TrigFunctions + tgindx[i], tgindx[i],
relinfo->ri_TrigFunctions,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate)); GetPerTupleMemoryContext(estate));
if (newtuple) if (newtuple)
@ -1501,11 +1508,6 @@ ExecBRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
if (newSlot != NULL) if (newSlot != NULL)
intuple = newtuple = ExecRemoveJunk(estate->es_junkFilter, newSlot); intuple = newtuple = ExecRemoveJunk(estate->es_junkFilter, newSlot);
/* Allocate cache space for fmgr lookup info, if not done yet */
if (relinfo->ri_TrigFunctions == NULL)
relinfo->ri_TrigFunctions = (FmgrInfo *)
palloc0(trigdesc->numtriggers * sizeof(FmgrInfo));
LocTriggerData.type = T_TriggerData; LocTriggerData.type = T_TriggerData;
LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE | LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
TRIGGER_EVENT_ROW | TRIGGER_EVENT_ROW |
@ -1523,7 +1525,9 @@ ExecBRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
LocTriggerData.tg_newtuplebuf = InvalidBuffer; LocTriggerData.tg_newtuplebuf = InvalidBuffer;
LocTriggerData.tg_trigger = trigger; LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData, newtuple = ExecCallTriggerFunc(&LocTriggerData,
relinfo->ri_TrigFunctions + tgindx[i], tgindx[i],
relinfo->ri_TrigFunctions,
relinfo->ri_TrigInstrument,
GetPerTupleMemoryContext(estate)); GetPerTupleMemoryContext(estate));
if (oldtuple != newtuple && oldtuple != intuple) if (oldtuple != newtuple && oldtuple != intuple)
heap_freetuple(oldtuple); heap_freetuple(oldtuple);
@ -1798,6 +1802,7 @@ static AfterTriggers afterTriggers;
static void AfterTriggerExecute(AfterTriggerEvent event, static void AfterTriggerExecute(AfterTriggerEvent event,
Relation rel, TriggerDesc *trigdesc, Relation rel, TriggerDesc *trigdesc,
FmgrInfo *finfo, FmgrInfo *finfo,
Instrumentation *instr,
MemoryContext per_tuple_context); MemoryContext per_tuple_context);
static SetConstraintState SetConstraintStateCreate(int numalloc); static SetConstraintState SetConstraintStateCreate(int numalloc);
static SetConstraintState SetConstraintStateCopy(SetConstraintState state); static SetConstraintState SetConstraintStateCopy(SetConstraintState state);
@ -1886,18 +1891,22 @@ afterTriggerAddEvent(AfterTriggerEvent event)
* *
* Frequently, this will be fired many times in a row for triggers of * Frequently, this will be fired many times in a row for triggers of
* a single relation. Therefore, we cache the open relation and provide * a single relation. Therefore, we cache the open relation and provide
* fmgr lookup cache space at the caller level. * fmgr lookup cache space at the caller level. (For triggers fired at
* the end of a query, we can even piggyback on the executor's state.)
* *
* event: event currently being fired. * event: event currently being fired.
* rel: open relation for event. * rel: open relation for event.
* trigdesc: working copy of rel's trigger info. * trigdesc: working copy of rel's trigger info.
* finfo: array of fmgr lookup cache entries (one per trigger in trigdesc). * finfo: array of fmgr lookup cache entries (one per trigger in trigdesc).
* instr: array of EXPLAIN ANALYZE instrumentation nodes (one per trigger),
* or NULL if no instrumentation is wanted.
* per_tuple_context: memory context to call trigger function in. * per_tuple_context: memory context to call trigger function in.
* ---------- * ----------
*/ */
static void static void
AfterTriggerExecute(AfterTriggerEvent event, AfterTriggerExecute(AfterTriggerEvent event,
Relation rel, TriggerDesc *trigdesc, FmgrInfo *finfo, Relation rel, TriggerDesc *trigdesc,
FmgrInfo *finfo, Instrumentation *instr,
MemoryContext per_tuple_context) MemoryContext per_tuple_context)
{ {
Oid tgoid = event->ate_tgoid; Oid tgoid = event->ate_tgoid;
@ -1909,6 +1918,28 @@ AfterTriggerExecute(AfterTriggerEvent event,
Buffer newbuffer = InvalidBuffer; Buffer newbuffer = InvalidBuffer;
int tgindx; int tgindx;
/*
* Locate trigger in trigdesc.
*/
LocTriggerData.tg_trigger = NULL;
for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
{
if (trigdesc->triggers[tgindx].tgoid == tgoid)
{
LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
break;
}
}
if (LocTriggerData.tg_trigger == NULL)
elog(ERROR, "could not find trigger %u", tgoid);
/*
* If doing EXPLAIN ANALYZE, start charging time to this trigger.
* We want to include time spent re-fetching tuples in the trigger cost.
*/
if (instr)
InstrStartNode(instr + tgindx);
/* /*
* Fetch the required OLD and NEW tuples. * Fetch the required OLD and NEW tuples.
*/ */
@ -1934,18 +1965,6 @@ AfterTriggerExecute(AfterTriggerEvent event,
event->ate_event & (TRIGGER_EVENT_OPMASK | TRIGGER_EVENT_ROW); event->ate_event & (TRIGGER_EVENT_OPMASK | TRIGGER_EVENT_ROW);
LocTriggerData.tg_relation = rel; LocTriggerData.tg_relation = rel;
LocTriggerData.tg_trigger = NULL;
for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++)
{
if (trigdesc->triggers[tgindx].tgoid == tgoid)
{
LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]);
break;
}
}
if (LocTriggerData.tg_trigger == NULL)
elog(ERROR, "could not find trigger %u", tgoid);
switch (event->ate_event & TRIGGER_EVENT_OPMASK) switch (event->ate_event & TRIGGER_EVENT_OPMASK)
{ {
case TRIGGER_EVENT_INSERT: case TRIGGER_EVENT_INSERT:
@ -1973,11 +1992,13 @@ AfterTriggerExecute(AfterTriggerEvent event,
MemoryContextReset(per_tuple_context); MemoryContextReset(per_tuple_context);
/* /*
* Call the trigger and throw away any eventually returned updated * Call the trigger and throw away any possibly returned updated
* tuple. * tuple. (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
*/ */
rettuple = ExecCallTriggerFunc(&LocTriggerData, rettuple = ExecCallTriggerFunc(&LocTriggerData,
finfo + tgindx, tgindx,
finfo,
NULL,
per_tuple_context); per_tuple_context);
if (rettuple != NULL && rettuple != &oldtuple && rettuple != &newtuple) if (rettuple != NULL && rettuple != &oldtuple && rettuple != &newtuple)
heap_freetuple(rettuple); heap_freetuple(rettuple);
@ -1989,6 +2010,13 @@ AfterTriggerExecute(AfterTriggerEvent event,
ReleaseBuffer(oldbuffer); ReleaseBuffer(oldbuffer);
if (newbuffer != InvalidBuffer) if (newbuffer != InvalidBuffer)
ReleaseBuffer(newbuffer); ReleaseBuffer(newbuffer);
/*
* If doing EXPLAIN ANALYZE, stop charging time to this trigger,
* and count one "tuple returned" (really the number of firings).
*/
if (instr)
InstrStopNode(instr + tgindx, true);
} }
@ -2093,6 +2121,12 @@ afterTriggerMarkEvents(AfterTriggerEventList *events,
* Scan the given event list for events that are marked as to be fired * Scan the given event list for events that are marked as to be fired
* in the current firing cycle, and fire them. * 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.
*
* When delete_ok is TRUE, it's okay to delete fully-processed events. * When delete_ok is TRUE, it's okay to delete fully-processed events.
* The events list pointers are updated. * The events list pointers are updated.
* ---------- * ----------
@ -2100,6 +2134,7 @@ afterTriggerMarkEvents(AfterTriggerEventList *events,
static void static void
afterTriggerInvokeEvents(AfterTriggerEventList *events, afterTriggerInvokeEvents(AfterTriggerEventList *events,
CommandId firing_id, CommandId firing_id,
EState *estate,
bool delete_ok) bool delete_ok)
{ {
AfterTriggerEvent event, AfterTriggerEvent event,
@ -2108,6 +2143,7 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
Relation rel = NULL; Relation rel = NULL;
TriggerDesc *trigdesc = NULL; TriggerDesc *trigdesc = NULL;
FmgrInfo *finfo = NULL; FmgrInfo *finfo = NULL;
Instrumentation *instr = NULL;
/* Make a per-tuple memory context for trigger function calls */ /* Make a per-tuple memory context for trigger function calls */
per_tuple_context = per_tuple_context =
@ -2136,35 +2172,65 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
*/ */
if (rel == NULL || rel->rd_id != event->ate_relid) if (rel == NULL || rel->rd_id != event->ate_relid)
{ {
if (rel) if (estate)
heap_close(rel, NoLock); {
if (trigdesc) /* Find target relation among estate's result rels */
FreeTriggerDesc(trigdesc); ResultRelInfo *rInfo;
if (finfo) int nr;
pfree(finfo);
/* rInfo = estate->es_result_relations;
* We assume that an appropriate lock is still held by nr = estate->es_num_result_relations;
* the executor, so grab no new lock here. while (nr > 0)
*/ {
rel = heap_open(event->ate_relid, NoLock); if (rInfo->ri_RelationDesc->rd_id == event->ate_relid)
break;
rInfo++;
nr--;
}
if (nr <= 0) /* should not happen */
elog(ERROR, "could not find relation %u among query result relations",
event->ate_relid);
rel = rInfo->ri_RelationDesc;
trigdesc = rInfo->ri_TrigDesc;
finfo = rInfo->ri_TrigFunctions;
instr = rInfo->ri_TrigInstrument;
}
else
{
/* Hard way: we manage the resources for ourselves */
if (rel)
heap_close(rel, NoLock);
if (trigdesc)
FreeTriggerDesc(trigdesc);
if (finfo)
pfree(finfo);
Assert(instr == NULL); /* never used in this case */
/* /*
* Copy relation's trigger info so that we have a * We assume that an appropriate lock is still held by
* stable copy no matter what the called triggers do. * the executor, so grab no new lock here.
*/ */
trigdesc = CopyTriggerDesc(rel->trigdesc); rel = heap_open(event->ate_relid, NoLock);
if (trigdesc == NULL) /* should not happen */ /*
elog(ERROR, "relation %u has no triggers", * Copy relation's trigger info so that we have a
event->ate_relid); * stable copy no matter what the called triggers do.
*/
trigdesc = CopyTriggerDesc(rel->trigdesc);
/* if (trigdesc == NULL) /* should not happen */
* Allocate space to cache fmgr lookup info for elog(ERROR, "relation %u has no triggers",
* triggers. event->ate_relid);
*/
finfo = (FmgrInfo *) /*
palloc0(trigdesc->numtriggers * sizeof(FmgrInfo)); * Allocate space to cache fmgr lookup info for
* triggers.
*/
finfo = (FmgrInfo *)
palloc0(trigdesc->numtriggers * sizeof(FmgrInfo));
/* Never any EXPLAIN info in this case */
}
} }
/* /*
@ -2172,7 +2238,7 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
* set, so recursive examinations of the event list won't try * set, so recursive examinations of the event list won't try
* to re-fire it. * to re-fire it.
*/ */
AfterTriggerExecute(event, rel, trigdesc, finfo, AfterTriggerExecute(event, rel, trigdesc, finfo, instr,
per_tuple_context); per_tuple_context);
/* /*
@ -2214,12 +2280,16 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
events->tail = prev_event; events->tail = prev_event;
/* Release working resources */ /* Release working resources */
if (rel) if (!estate)
heap_close(rel, NoLock); {
if (trigdesc) if (rel)
FreeTriggerDesc(trigdesc); heap_close(rel, NoLock);
if (finfo) if (trigdesc)
pfree(finfo); FreeTriggerDesc(trigdesc);
if (finfo)
pfree(finfo);
Assert(instr == NULL); /* never used in this case */
}
MemoryContextDelete(per_tuple_context); MemoryContextDelete(per_tuple_context);
} }
@ -2308,10 +2378,14 @@ AfterTriggerBeginQuery(void)
* Called after one query has been completely processed. At this time * Called after one query has been completely processed. At this time
* we invoke all AFTER IMMEDIATE trigger events queued by the query, and * we invoke all AFTER IMMEDIATE trigger events queued by the query, and
* transfer deferred trigger events to the global deferred-trigger list. * transfer deferred trigger events to the global deferred-trigger list.
*
* Note that this should be called just BEFORE closing down the executor
* with ExecutorEnd, because we make use of the EState's info about
* target relations.
* ---------- * ----------
*/ */
void void
AfterTriggerEndQuery(void) AfterTriggerEndQuery(EState *estate)
{ {
AfterTriggerEventList *events; AfterTriggerEventList *events;
@ -2339,7 +2413,7 @@ AfterTriggerEndQuery(void)
CommandId firing_id = afterTriggers->firing_counter++; CommandId firing_id = afterTriggers->firing_counter++;
/* OK to delete the immediate events after processing them */ /* OK to delete the immediate events after processing them */
afterTriggerInvokeEvents(events, firing_id, true); afterTriggerInvokeEvents(events, firing_id, estate, true);
} }
afterTriggers->query_depth--; afterTriggers->query_depth--;
@ -2381,7 +2455,7 @@ AfterTriggerEndXact(void)
{ {
CommandId firing_id = afterTriggers->firing_counter++; CommandId firing_id = afterTriggers->firing_counter++;
afterTriggerInvokeEvents(events, firing_id, true); afterTriggerInvokeEvents(events, firing_id, NULL, true);
} }
/* /*
@ -2838,7 +2912,7 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
* level, but we'd better not if inside a subtransaction, since * level, but we'd better not if inside a subtransaction, since
* the subtransaction could later get rolled back. * the subtransaction could later get rolled back.
*/ */
afterTriggerInvokeEvents(events, firing_id, afterTriggerInvokeEvents(events, firing_id, NULL,
!IsSubTransaction()); !IsSubTransaction());
} }
} }

View File

@ -26,7 +26,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.243 2005/03/20 23:40:25 neilc Exp $ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.244 2005/03/25 21:57:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -39,6 +39,7 @@
#include "commands/trigger.h" #include "commands/trigger.h"
#include "executor/execdebug.h" #include "executor/execdebug.h"
#include "executor/execdefs.h" #include "executor/execdefs.h"
#include "executor/instrument.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
#include "optimizer/var.h" #include "optimizer/var.h"
@ -69,7 +70,8 @@ static void InitPlan(QueryDesc *queryDesc, bool explainOnly);
static void initResultRelInfo(ResultRelInfo *resultRelInfo, static void initResultRelInfo(ResultRelInfo *resultRelInfo,
Index resultRelationIndex, Index resultRelationIndex,
List *rangeTable, List *rangeTable,
CmdType operation); CmdType operation,
bool doInstrument);
static TupleTableSlot *ExecutePlan(EState *estate, PlanState *planstate, static TupleTableSlot *ExecutePlan(EState *estate, PlanState *planstate,
CmdType operation, CmdType operation,
long numberTuples, long numberTuples,
@ -508,7 +510,8 @@ InitPlan(QueryDesc *queryDesc, bool explainOnly)
initResultRelInfo(resultRelInfo, initResultRelInfo(resultRelInfo,
lfirst_int(l), lfirst_int(l),
rangeTable, rangeTable,
operation); operation,
estate->es_instrument);
resultRelInfo++; resultRelInfo++;
} }
} }
@ -523,7 +526,8 @@ InitPlan(QueryDesc *queryDesc, bool explainOnly)
initResultRelInfo(resultRelInfos, initResultRelInfo(resultRelInfos,
parseTree->resultRelation, parseTree->resultRelation,
rangeTable, rangeTable,
operation); operation,
estate->es_instrument);
} }
estate->es_result_relations = resultRelInfos; estate->es_result_relations = resultRelInfos;
@ -798,7 +802,8 @@ static void
initResultRelInfo(ResultRelInfo *resultRelInfo, initResultRelInfo(ResultRelInfo *resultRelInfo,
Index resultRelationIndex, Index resultRelationIndex,
List *rangeTable, List *rangeTable,
CmdType operation) CmdType operation,
bool doInstrument)
{ {
Oid resultRelationOid; Oid resultRelationOid;
Relation resultRelationDesc; Relation resultRelationDesc;
@ -837,7 +842,22 @@ initResultRelInfo(ResultRelInfo *resultRelInfo,
resultRelInfo->ri_IndexRelationInfo = NULL; resultRelInfo->ri_IndexRelationInfo = NULL;
/* make a copy so as not to depend on relcache info not changing... */ /* make a copy so as not to depend on relcache info not changing... */
resultRelInfo->ri_TrigDesc = CopyTriggerDesc(resultRelationDesc->trigdesc); resultRelInfo->ri_TrigDesc = CopyTriggerDesc(resultRelationDesc->trigdesc);
resultRelInfo->ri_TrigFunctions = NULL; if (resultRelInfo->ri_TrigDesc)
{
int n = resultRelInfo->ri_TrigDesc->numtriggers;
resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
palloc0(n * sizeof(FmgrInfo));
if (doInstrument)
resultRelInfo->ri_TrigInstrument = InstrAlloc(n);
else
resultRelInfo->ri_TrigInstrument = NULL;
}
else
{
resultRelInfo->ri_TrigFunctions = NULL;
resultRelInfo->ri_TrigInstrument = NULL;
}
resultRelInfo->ri_ConstraintExprs = NULL; resultRelInfo->ri_ConstraintExprs = NULL;
resultRelInfo->ri_junkFilter = NULL; resultRelInfo->ri_junkFilter = NULL;

View File

@ -12,7 +12,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execProcnode.c,v 1.46 2004/12/31 21:59:45 pgsql Exp $ * $PostgreSQL: pgsql/src/backend/executor/execProcnode.c,v 1.47 2005/03/25 21:57:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -252,7 +252,7 @@ ExecInitNode(Plan *node, EState *estate)
/* Set up instrumentation for this node if requested */ /* Set up instrumentation for this node if requested */
if (estate->es_instrument) if (estate->es_instrument)
result->instrument = InstrAlloc(); result->instrument = InstrAlloc(1);
return result; return result;
} }

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.92 2005/03/16 21:38:07 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.93 2005/03/25 21:57:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -399,8 +399,8 @@ postquel_end(execution_state *es, SQLFunctionCachePtr fcache)
{ {
ActiveSnapshot = es->qd->snapshot; ActiveSnapshot = es->qd->snapshot;
AfterTriggerEndQuery(es->qd->estate);
ExecutorEnd(es->qd); ExecutorEnd(es->qd);
AfterTriggerEndQuery();
} }
PG_CATCH(); PG_CATCH();
{ {

View File

@ -7,7 +7,7 @@
* Copyright (c) 2001-2005, PostgreSQL Global Development Group * Copyright (c) 2001-2005, PostgreSQL Global Development Group
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/instrument.c,v 1.10 2005/03/20 22:27:51 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/instrument.c,v 1.11 2005/03/25 21:57:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -18,13 +18,13 @@
#include "executor/instrument.h" #include "executor/instrument.h"
/* Allocate new instrumentation structure */ /* Allocate new instrumentation structure(s) */
Instrumentation * Instrumentation *
InstrAlloc(void) InstrAlloc(int n)
{ {
Instrumentation *instr = palloc(sizeof(Instrumentation)); Instrumentation *instr = palloc0(n * sizeof(Instrumentation));
memset(instr, 0, sizeof(Instrumentation)); /* we don't need to do any initialization except zero 'em */
return instr; return instr;
} }

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.135 2005/03/16 21:38:08 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.136 2005/03/25 21:57:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -1546,10 +1546,10 @@ _SPI_pquery(QueryDesc *queryDesc, int tcount)
elog(ERROR, "consistency check on SPI tuple count failed"); elog(ERROR, "consistency check on SPI tuple count failed");
} }
ExecutorEnd(queryDesc);
/* Take care of any queued AFTER triggers */ /* Take care of any queued AFTER triggers */
AfterTriggerEndQuery(); AfterTriggerEndQuery(queryDesc->estate);
ExecutorEnd(queryDesc);
if (queryDesc->dest->mydest == SPI) if (queryDesc->dest->mydest == SPI)
{ {

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.92 2005/03/16 21:38:08 tgl Exp $ * $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.93 2005/03/25 21:57:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -206,14 +206,14 @@ ProcessQuery(Query *parsetree,
} }
} }
/* Now take care of any queued AFTER triggers */
AfterTriggerEndQuery(queryDesc->estate);
/* /*
* Now, we close down all the scans and free allocated resources. * Now, we close down all the scans and free allocated resources.
*/ */
ExecutorEnd(queryDesc); ExecutorEnd(queryDesc);
/* And take care of any queued AFTER triggers */
AfterTriggerEndQuery();
FreeQueryDesc(queryDesc); FreeQueryDesc(queryDesc);
FreeSnapshot(ActiveSnapshot); FreeSnapshot(ActiveSnapshot);

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.14 2004/12/31 22:03:24 pgsql Exp $ * $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.15 2005/03/25 21:57:59 tgl Exp $
* *
* NOTES * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
@ -181,4 +181,6 @@ extern char *ChooseConstraintName(const char *name1, const char *name2,
const char *label, Oid namespace, const char *label, Oid namespace,
List *others); List *others);
extern char *GetConstraintNameForTrigger(Oid triggerId);
#endif /* PG_CONSTRAINT_H */ #endif /* PG_CONSTRAINT_H */

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.51 2004/12/31 22:03:28 pgsql Exp $ * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.52 2005/03/25 21:57:59 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -156,7 +156,7 @@ extern void ExecARUpdateTriggers(EState *estate,
extern void AfterTriggerBeginXact(void); extern void AfterTriggerBeginXact(void);
extern void AfterTriggerBeginQuery(void); extern void AfterTriggerBeginQuery(void);
extern void AfterTriggerEndQuery(void); extern void AfterTriggerEndQuery(EState *estate);
extern void AfterTriggerEndXact(void); extern void AfterTriggerEndXact(void);
extern void AfterTriggerAbortXact(void); extern void AfterTriggerAbortXact(void);
extern void AfterTriggerBeginSubXact(void); extern void AfterTriggerBeginSubXact(void);

View File

@ -6,7 +6,7 @@
* *
* Copyright (c) 2001-2005, PostgreSQL Global Development Group * Copyright (c) 2001-2005, PostgreSQL Global Development Group
* *
* $PostgreSQL: pgsql/src/include/executor/instrument.h,v 1.9 2005/03/20 22:27:52 tgl Exp $ * $PostgreSQL: pgsql/src/include/executor/instrument.h,v 1.10 2005/03/25 21:57:59 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -70,7 +70,7 @@ typedef struct Instrumentation
double nloops; /* # of run cycles for this node */ double nloops; /* # of run cycles for this node */
} Instrumentation; } Instrumentation;
extern Instrumentation *InstrAlloc(void); extern Instrumentation *InstrAlloc(int n);
extern void InstrStartNode(Instrumentation *instr); extern void InstrStartNode(Instrumentation *instr);
extern void InstrStopNode(Instrumentation *instr, bool returnedTuple); extern void InstrStopNode(Instrumentation *instr, bool returnedTuple);
extern void InstrEndLoop(Instrumentation *instr); extern void InstrEndLoop(Instrumentation *instr);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.124 2005/03/16 21:38:10 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.125 2005/03/25 21:58:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -261,6 +261,7 @@ typedef struct JunkFilter
* IndexRelationInfo array of key/attr info for indices * IndexRelationInfo array of key/attr info for indices
* TrigDesc triggers to be fired, if any * TrigDesc triggers to be fired, if any
* TrigFunctions cached lookup info for trigger functions * TrigFunctions cached lookup info for trigger functions
* TrigInstrument optional runtime measurements for triggers
* ConstraintExprs array of constraint-checking expr states * ConstraintExprs array of constraint-checking expr states
* junkFilter for removing junk attributes from tuples * junkFilter for removing junk attributes from tuples
* ---------------- * ----------------
@ -275,6 +276,7 @@ typedef struct ResultRelInfo
IndexInfo **ri_IndexRelationInfo; IndexInfo **ri_IndexRelationInfo;
TriggerDesc *ri_TrigDesc; TriggerDesc *ri_TrigDesc;
FmgrInfo *ri_TrigFunctions; FmgrInfo *ri_TrigFunctions;
struct Instrumentation *ri_TrigInstrument;
List **ri_ConstraintExprs; List **ri_ConstraintExprs;
JunkFilter *ri_junkFilter; JunkFilter *ri_junkFilter;
} ResultRelInfo; } ResultRelInfo;