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:
parent
08890b407e
commit
adb1a6e95b
@ -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 < 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>
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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 */
|
||||||
|
@ -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
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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);
|
||||||
|
@ -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 */
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user