diff options
Diffstat (limited to 'src/backend/executor')
-rw-r--r-- | src/backend/executor/README | 5 | ||||
-rw-r--r-- | src/backend/executor/execMain.c | 135 | ||||
-rw-r--r-- | src/backend/executor/execUtils.c | 4 | ||||
-rw-r--r-- | src/backend/executor/functions.c | 25 | ||||
-rw-r--r-- | src/backend/executor/spi.c | 13 |
5 files changed, 138 insertions, 44 deletions
diff --git a/src/backend/executor/README b/src/backend/executor/README index fec191d9b07..8afa1e3e4a7 100644 --- a/src/backend/executor/README +++ b/src/backend/executor/README @@ -95,12 +95,17 @@ This is a sketch of control flow for full query processing: CreateExprContext creates per-tuple context ExecInitExpr + AfterTriggerBeginQuery ExecutorRun ExecProcNode --- recursively called in per-query context ExecEvalExpr --- called in per-tuple context ResetExprContext --- to free memory + ExecutorFinish + ExecPostprocessPlan --- run any unfinished ModifyTable nodes + AfterTriggerEndQuery + ExecutorEnd ExecEndNode --- recursively releases resources FreeExecutorState 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 diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 511c74eeafd..6cf692a956d 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -137,9 +137,11 @@ CreateExecutorState(void) estate->es_processed = 0; estate->es_lastoid = InvalidOid; - estate->es_instrument = false; + estate->es_top_eflags = 0; + estate->es_instrument = 0; estate->es_select_into = false; estate->es_into_oids = false; + estate->es_finished = false; estate->es_exprcontexts = NIL; diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 79bbe6bc762..d5385438e3b 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -205,7 +205,8 @@ init_execution_state(List *queryTree_list, if (ps->commandType == CMD_SELECT && ps->utilityStmt == NULL && - ps->intoClause == NULL) + ps->intoClause == NULL && + !ps->hasModifyingCTE) fcache->lazyEval = lasttages->lazyEval = true; } } @@ -431,14 +432,19 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache) if (es->qd->utilitystmt == NULL) { /* - * Only set up to collect queued triggers if it's not a SELECT. This - * isn't just an optimization, but is necessary in case a SELECT - * returns multiple rows to caller --- we mustn't exit from the - * function execution with a stacked AfterTrigger level still active. + * In lazyEval mode, do not let the executor set up an AfterTrigger + * context. This is necessary not just an optimization, because we + * mustn't exit from the function execution with a stacked + * AfterTrigger level still active. We are careful not to select + * lazyEval mode for any statement that could possibly queue triggers. */ - if (es->qd->operation != CMD_SELECT) - AfterTriggerBeginQuery(); - ExecutorStart(es->qd, 0); + int eflags; + + if (es->lazyEval) + eflags = EXEC_FLAG_SKIP_TRIGGERS; + else + eflags = 0; /* default run-to-completion flags */ + ExecutorStart(es->qd, eflags); } es->status = F_EXEC_RUN; @@ -499,8 +505,7 @@ postquel_end(execution_state *es) /* Make our snapshot the active one for any called functions */ PushActiveSnapshot(es->qd->snapshot); - if (es->qd->operation != CMD_SELECT) - AfterTriggerEndQuery(es->qd->estate); + ExecutorFinish(es->qd); ExecutorEnd(es->qd); PopActiveSnapshot(); diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 4942de01550..0865e3ebef2 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -2041,6 +2041,7 @@ static int _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, long tcount) { int operation = queryDesc->operation; + int eflags; int res; switch (operation) @@ -2084,10 +2085,13 @@ _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, long tcount) ResetUsage(); #endif + /* Select execution options */ if (fire_triggers) - AfterTriggerBeginQuery(); + eflags = 0; /* default run-to-completion flags */ + else + eflags = EXEC_FLAG_SKIP_TRIGGERS; - ExecutorStart(queryDesc, 0); + ExecutorStart(queryDesc, eflags); ExecutorRun(queryDesc, ForwardScanDirection, tcount); @@ -2101,10 +2105,7 @@ _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, long tcount) elog(ERROR, "consistency check on SPI tuple count failed"); } - /* Take care of any queued AFTER triggers */ - if (fire_triggers) - AfterTriggerEndQuery(queryDesc->estate); - + ExecutorFinish(queryDesc); ExecutorEnd(queryDesc); /* FreeQueryDesc is done by the caller */ |