aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/execMain.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/execMain.c')
-rw-r--r--src/backend/executor/execMain.c130
1 files changed, 118 insertions, 12 deletions
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index dfd1a84ab77..322c1d1a27d 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.295 2007/06/11 01:16:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.296 2007/08/15 21:39:50 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -66,8 +66,8 @@ typedef struct evalPlanQual
/* decls for local routines only used within this module */
static void InitPlan(QueryDesc *queryDesc, int eflags);
static void initResultRelInfo(ResultRelInfo *resultRelInfo,
+ Relation resultRelationDesc,
Index resultRelationIndex,
- List *rangeTable,
CmdType operation,
bool doInstrument);
static void ExecEndPlan(PlanState *planstate, EState *estate);
@@ -494,9 +494,15 @@ InitPlan(QueryDesc *queryDesc, int eflags)
resultRelInfo = resultRelInfos;
foreach(l, resultRelations)
{
+ Index resultRelationIndex = lfirst_int(l);
+ Oid resultRelationOid;
+ Relation resultRelation;
+
+ resultRelationOid = getrelid(resultRelationIndex, rangeTable);
+ resultRelation = heap_open(resultRelationOid, RowExclusiveLock);
initResultRelInfo(resultRelInfo,
- lfirst_int(l),
- rangeTable,
+ resultRelation,
+ resultRelationIndex,
operation,
estate->es_instrument);
resultRelInfo++;
@@ -831,19 +837,20 @@ InitPlan(QueryDesc *queryDesc, int eflags)
*/
static void
initResultRelInfo(ResultRelInfo *resultRelInfo,
+ Relation resultRelationDesc,
Index resultRelationIndex,
- List *rangeTable,
CmdType operation,
bool doInstrument)
{
- Oid resultRelationOid;
- Relation resultRelationDesc;
-
- resultRelationOid = getrelid(resultRelationIndex, rangeTable);
- resultRelationDesc = heap_open(resultRelationOid, RowExclusiveLock);
-
+ /*
+ * Check valid relkind ... parser and/or planner should have noticed
+ * this already, but let's make sure.
+ */
switch (resultRelationDesc->rd_rel->relkind)
{
+ case RELKIND_RELATION:
+ /* OK */
+ break;
case RELKIND_SEQUENCE:
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -862,8 +869,15 @@ initResultRelInfo(ResultRelInfo *resultRelInfo,
errmsg("cannot change view \"%s\"",
RelationGetRelationName(resultRelationDesc))));
break;
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot change relation \"%s\"",
+ RelationGetRelationName(resultRelationDesc))));
+ break;
}
+ /* OK, fill in the node */
MemSet(resultRelInfo, 0, sizeof(ResultRelInfo));
resultRelInfo->type = T_ResultRelInfo;
resultRelInfo->ri_RangeTableIndex = resultRelationIndex;
@@ -905,6 +919,76 @@ initResultRelInfo(ResultRelInfo *resultRelInfo,
}
/*
+ * ExecGetTriggerResultRel
+ *
+ * Get a ResultRelInfo for a trigger target relation. Most of the time,
+ * triggers are fired on one of the result relations of the query, and so
+ * we can just return a member of the es_result_relations array. (Note: in
+ * self-join situations there might be multiple members with the same OID;
+ * if so it doesn't matter which one we pick.) However, it is sometimes
+ * necessary to fire triggers on other relations; this happens mainly when an
+ * RI update trigger queues additional triggers on other relations, which will
+ * be processed in the context of the outer query. For efficiency's sake,
+ * we want to have a ResultRelInfo for those triggers too; that can avoid
+ * repeated re-opening of the relation. (It also provides a way for EXPLAIN
+ * ANALYZE to report the runtimes of such triggers.) So we make additional
+ * ResultRelInfo's as needed, and save them in es_trig_target_relations.
+ */
+ResultRelInfo *
+ExecGetTriggerResultRel(EState *estate, Oid relid)
+{
+ ResultRelInfo *rInfo;
+ int nr;
+ ListCell *l;
+ Relation rel;
+ MemoryContext oldcontext;
+
+ /* First, search through the query result relations */
+ rInfo = estate->es_result_relations;
+ nr = estate->es_num_result_relations;
+ while (nr > 0)
+ {
+ if (RelationGetRelid(rInfo->ri_RelationDesc) == relid)
+ return rInfo;
+ rInfo++;
+ nr--;
+ }
+ /* Nope, but maybe we already made an extra ResultRelInfo for it */
+ foreach(l, estate->es_trig_target_relations)
+ {
+ rInfo = (ResultRelInfo *) lfirst(l);
+ if (RelationGetRelid(rInfo->ri_RelationDesc) == relid)
+ return rInfo;
+ }
+ /* Nope, so we need a new one */
+
+ /*
+ * Open the target relation's relcache entry. We assume that an
+ * appropriate lock is still held by the backend from whenever the
+ * trigger event got queued, so we need take no new lock here.
+ */
+ rel = heap_open(relid, NoLock);
+
+ /*
+ * Make the new entry in the right context. Currently, we don't need
+ * any index information in ResultRelInfos used only for triggers,
+ * so tell initResultRelInfo it's a DELETE.
+ */
+ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+ rInfo = makeNode(ResultRelInfo);
+ initResultRelInfo(rInfo,
+ rel,
+ 0, /* dummy rangetable index */
+ CMD_DELETE,
+ estate->es_instrument);
+ estate->es_trig_target_relations =
+ lappend(estate->es_trig_target_relations, rInfo);
+ MemoryContextSwitchTo(oldcontext);
+
+ return rInfo;
+}
+
+/*
* ExecContextForcesOids
*
* This is pretty grotty: when doing INSERT, UPDATE, or SELECT INTO,
@@ -1020,6 +1104,17 @@ ExecEndPlan(PlanState *planstate, EState *estate)
}
/*
+ * likewise close any trigger target relations
+ */
+ foreach(l, estate->es_trig_target_relations)
+ {
+ resultRelInfo = (ResultRelInfo *) lfirst(l);
+ /* Close indices and then the relation itself */
+ ExecCloseIndices(resultRelInfo);
+ heap_close(resultRelInfo->ri_RelationDesc, NoLock);
+ }
+
+ /*
* close any relations selected FOR UPDATE/FOR SHARE, again keeping locks
*/
foreach(l, estate->es_rowMarks)
@@ -2267,6 +2362,7 @@ EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq)
epqstate->es_num_result_relations = estate->es_num_result_relations;
epqstate->es_result_relation_info = estate->es_result_relation_info;
epqstate->es_junkFilter = estate->es_junkFilter;
+ /* es_trig_target_relations must NOT be copied */
epqstate->es_into_relation_descriptor = estate->es_into_relation_descriptor;
epqstate->es_into_relation_use_wal = estate->es_into_relation_use_wal;
epqstate->es_param_list_info = estate->es_param_list_info;
@@ -2331,7 +2427,8 @@ EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq)
*
* This is a cut-down version of ExecutorEnd(); basically we want to do most
* of the normal cleanup, but *not* close result relations (which we are
- * just sharing from the outer query).
+ * just sharing from the outer query). We do, however, have to close any
+ * trigger target relations that got opened, since those are not shared.
*/
static void
EvalPlanQualStop(evalPlanQual *epq)
@@ -2360,6 +2457,15 @@ EvalPlanQualStop(evalPlanQual *epq)
epqstate->es_evTuple[epq->rti - 1] = NULL;
}
+ foreach(l, epqstate->es_trig_target_relations)
+ {
+ ResultRelInfo *resultRelInfo = (ResultRelInfo *) lfirst(l);
+
+ /* Close indices and then the relation itself */
+ ExecCloseIndices(resultRelInfo);
+ heap_close(resultRelInfo->ri_RelationDesc, NoLock);
+ }
+
MemoryContextSwitchTo(oldcontext);
FreeExecutorState(epqstate);