aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/execMain.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2011-02-27 13:43:29 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2011-02-27 13:44:12 -0500
commita874fe7b4c890d1fe3455215a83ca777867beadd (patch)
tree04b7870100a76bb79470abaf0f971550043590d7 /src/backend/executor/execMain.c
parent67a5e727c8655496013b007d2fb6137fcc244b18 (diff)
downloadpostgresql-a874fe7b4c890d1fe3455215a83ca777867beadd.tar.gz
postgresql-a874fe7b4c890d1fe3455215a83ca777867beadd.zip
Refactor the executor's API to support data-modifying CTEs better.
The originally committed patch for modifying CTEs didn't interact well with EXPLAIN, as noted by myself, and also had corner-case problems with triggers, as noted by Dean Rasheed. Those problems show it is really not practical for ExecutorEnd to call any user-defined code; so split the cleanup duties out into a new function ExecutorFinish, which must be called between the last ExecutorRun call and ExecutorEnd. Some Asserts have been added to these functions to help verify correct usage. It is no longer necessary for callers of the executor to call AfterTriggerBeginQuery/AfterTriggerEndQuery for themselves, as this is now done by ExecutorStart/ExecutorFinish respectively. If you really need to suppress that and do it for yourself, pass EXEC_FLAG_SKIP_TRIGGERS to ExecutorStart. Also, refactor portal commit processing to allow for the possibility that PortalDrop will invoke user-defined code. I think this is not actually necessary just yet, since the portal-execution-strategy logic forces any non-pure-SELECT query to be run to completion before we will consider committing. But it seems like good future-proofing.
Diffstat (limited to 'src/backend/executor/execMain.c')
-rw-r--r--src/backend/executor/execMain.c135
1 files changed, 108 insertions, 27 deletions
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 845fac1ae1c..caa9faea87f 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -6,20 +6,25 @@
* INTERFACE ROUTINES
* ExecutorStart()
* ExecutorRun()
+ * ExecutorFinish()
* ExecutorEnd()
*
- * The old ExecutorMain() has been replaced by ExecutorStart(),
- * ExecutorRun() and ExecutorEnd()
- *
- * These three procedures are the external interfaces to the executor.
+ * These four procedures are the external interface to the executor.
* In each case, the query descriptor is required as an argument.
*
- * ExecutorStart() must be called at the beginning of execution of any
- * query plan and ExecutorEnd() should always be called at the end of
- * execution of a plan.
+ * ExecutorStart must be called at the beginning of execution of any
+ * query plan and ExecutorEnd must always be called at the end of
+ * execution of a plan (unless it is aborted due to error).
*
* ExecutorRun accepts direction and count arguments that specify whether
* the plan is to be executed forwards, backwards, and for how many tuples.
+ * In some cases ExecutorRun may be called multiple times to process all
+ * the tuples for a plan. It is also acceptable to stop short of executing
+ * the whole plan (but only if it is a SELECT).
+ *
+ * ExecutorFinish must be called after the final ExecutorRun call and
+ * before ExecutorEnd. This can be omitted only in case of EXPLAIN,
+ * which should also omit ExecutorRun.
*
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
@@ -58,9 +63,10 @@
#include "utils/tqual.h"
-/* Hooks for plugins to get control in ExecutorStart/Run/End() */
+/* Hooks for plugins to get control in ExecutorStart/Run/Finish/End */
ExecutorStart_hook_type ExecutorStart_hook = NULL;
ExecutorRun_hook_type ExecutorRun_hook = NULL;
+ExecutorFinish_hook_type ExecutorFinish_hook = NULL;
ExecutorEnd_hook_type ExecutorEnd_hook = NULL;
/* Hook for plugin to get control in ExecCheckRTPerms() */
@@ -96,8 +102,8 @@ static void intorel_destroy(DestReceiver *self);
* This routine must be called at the beginning of any execution of any
* query plan
*
- * Takes a QueryDesc previously created by CreateQueryDesc (it's not real
- * clear why we bother to separate the two functions, but...). The tupDesc
+ * Takes a QueryDesc previously created by CreateQueryDesc (which is separate
+ * only because some places use QueryDescs for utility commands). The tupDesc
* field of the QueryDesc is filled in to describe the tuples that will be
* returned, and the internal fields (estate and planstate) are set up.
*
@@ -170,6 +176,15 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
queryDesc->plannedstmt->rowMarks != NIL ||
queryDesc->plannedstmt->hasModifyingCTE)
estate->es_output_cid = GetCurrentCommandId(true);
+
+ /*
+ * A SELECT without modifying CTEs can't possibly queue triggers,
+ * so force skip-triggers mode. This is just a marginal efficiency
+ * hack, since AfterTriggerBeginQuery/AfterTriggerEndQuery aren't
+ * all that expensive, but we might as well do it.
+ */
+ if (!queryDesc->plannedstmt->hasModifyingCTE)
+ eflags |= EXEC_FLAG_SKIP_TRIGGERS;
break;
case CMD_INSERT:
@@ -189,6 +204,7 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
*/
estate->es_snapshot = RegisterSnapshot(queryDesc->snapshot);
estate->es_crosscheck_snapshot = RegisterSnapshot(queryDesc->crosscheck_snapshot);
+ estate->es_top_eflags = eflags;
estate->es_instrument = queryDesc->instrument_options;
/*
@@ -196,6 +212,13 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
*/
InitPlan(queryDesc, eflags);
+ /*
+ * Set up an AFTER-trigger statement context, unless told not to, or
+ * unless it's EXPLAIN-only mode (when ExecutorFinish won't be called).
+ */
+ if (!(eflags & (EXEC_FLAG_SKIP_TRIGGERS | EXEC_FLAG_EXPLAIN_ONLY)))
+ AfterTriggerBeginQuery();
+
MemoryContextSwitchTo(oldcontext);
}
@@ -252,13 +275,14 @@ standard_ExecutorRun(QueryDesc *queryDesc,
estate = queryDesc->estate;
Assert(estate != NULL);
+ Assert(!(estate->es_top_eflags & EXEC_FLAG_EXPLAIN_ONLY));
/*
* Switch into per-query memory context
*/
oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
- /* Allow instrumentation of ExecutorRun overall runtime */
+ /* Allow instrumentation of Executor overall runtime */
if (queryDesc->totaltime)
InstrStartNode(queryDesc->totaltime);
@@ -305,6 +329,68 @@ standard_ExecutorRun(QueryDesc *queryDesc,
}
/* ----------------------------------------------------------------
+ * ExecutorFinish
+ *
+ * This routine must be called after the last ExecutorRun call.
+ * It performs cleanup such as firing AFTER triggers. It is
+ * separate from ExecutorEnd because EXPLAIN ANALYZE needs to
+ * include these actions in the total runtime.
+ *
+ * We provide a function hook variable that lets loadable plugins
+ * get control when ExecutorFinish is called. Such a plugin would
+ * normally call standard_ExecutorFinish().
+ *
+ * ----------------------------------------------------------------
+ */
+void
+ExecutorFinish(QueryDesc *queryDesc)
+{
+ if (ExecutorFinish_hook)
+ (*ExecutorFinish_hook) (queryDesc);
+ else
+ standard_ExecutorFinish(queryDesc);
+}
+
+void
+standard_ExecutorFinish(QueryDesc *queryDesc)
+{
+ EState *estate;
+ MemoryContext oldcontext;
+
+ /* sanity checks */
+ Assert(queryDesc != NULL);
+
+ estate = queryDesc->estate;
+
+ Assert(estate != NULL);
+ Assert(!(estate->es_top_eflags & EXEC_FLAG_EXPLAIN_ONLY));
+
+ /* This should be run once and only once per Executor instance */
+ Assert(!estate->es_finished);
+
+ /* Switch into per-query memory context */
+ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+ /* Allow instrumentation of Executor overall runtime */
+ if (queryDesc->totaltime)
+ InstrStartNode(queryDesc->totaltime);
+
+ /* Run ModifyTable nodes to completion */
+ ExecPostprocessPlan(estate);
+
+ /* Execute queued AFTER triggers, unless told not to */
+ if (!(estate->es_top_eflags & EXEC_FLAG_SKIP_TRIGGERS))
+ AfterTriggerEndQuery(estate);
+
+ if (queryDesc->totaltime)
+ InstrStopNode(queryDesc->totaltime, 0);
+
+ MemoryContextSwitchTo(oldcontext);
+
+ estate->es_finished = true;
+}
+
+/* ----------------------------------------------------------------
* ExecutorEnd
*
* This routine must be called at the end of execution of any
@@ -312,19 +398,13 @@ standard_ExecutorRun(QueryDesc *queryDesc,
*
* We provide a function hook variable that lets loadable plugins
* get control when ExecutorEnd is called. Such a plugin would
- * normally call standard_ExecutorEnd(). Because such hooks expect
- * to execute after all plan execution is done, we run
- * ExecPostprocessPlan before invoking the hook.
+ * normally call standard_ExecutorEnd().
*
* ----------------------------------------------------------------
*/
void
ExecutorEnd(QueryDesc *queryDesc)
{
- /* Let plan nodes do any final processing required */
- ExecPostprocessPlan(queryDesc->estate);
-
- /* Now close down */
if (ExecutorEnd_hook)
(*ExecutorEnd_hook) (queryDesc);
else
@@ -345,6 +425,14 @@ standard_ExecutorEnd(QueryDesc *queryDesc)
Assert(estate != NULL);
/*
+ * Check that ExecutorFinish was called, unless in EXPLAIN-only mode.
+ * This Assert is needed because ExecutorFinish is new as of 9.1, and
+ * callers might forget to call it.
+ */
+ Assert(estate->es_finished ||
+ (estate->es_top_eflags & EXEC_FLAG_EXPLAIN_ONLY));
+
+ /*
* Switch into per-query memory context to run ExecEndPlan
*/
oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
@@ -1141,15 +1229,9 @@ ExecContextForcesOids(PlanState *planstate, bool *hasoids)
static void
ExecPostprocessPlan(EState *estate)
{
- MemoryContext oldcontext;
ListCell *lc;
/*
- * Switch into per-query memory context
- */
- oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
-
- /*
* Make sure nodes run forward.
*/
estate->es_direction = ForwardScanDirection;
@@ -1176,8 +1258,6 @@ ExecPostprocessPlan(EState *estate)
break;
}
}
-
- MemoryContextSwitchTo(oldcontext);
}
/* ----------------------------------------------------------------
@@ -2081,10 +2161,11 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
estate->es_result_relation_info = parentestate->es_result_relation_info;
/* es_trig_target_relations must NOT be copied */
estate->es_rowMarks = parentestate->es_rowMarks;
+ estate->es_top_eflags = parentestate->es_top_eflags;
estate->es_instrument = parentestate->es_instrument;
estate->es_select_into = parentestate->es_select_into;
estate->es_into_oids = parentestate->es_into_oids;
- estate->es_auxmodifytables = NIL;
+ /* es_auxmodifytables must NOT be copied */
/*
* The external param list is simply shared from parent. The internal