diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 49fa4156de..47840d42eb 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.313 2008/08/25 22:42:32 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.314 2008/10/31 21:07:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -76,7 +76,7 @@ typedef struct evalPlanQual static void InitPlan(QueryDesc *queryDesc, int eflags); static void ExecCheckPlanOutput(Relation resultRel, List *targetList); static void ExecEndPlan(PlanState *planstate, EState *estate); -static TupleTableSlot *ExecutePlan(EState *estate, PlanState *planstate, +static void ExecutePlan(EState *estate, PlanState *planstate, CmdType operation, long numberTuples, ScanDirection direction, @@ -220,26 +220,28 @@ ExecutorStart(QueryDesc *queryDesc, int eflags) * Note: count = 0 is interpreted as no portal limit, i.e., run to * completion. * + * There is no return value, but output tuples (if any) are sent to + * the destination receiver specified in the QueryDesc; and the number + * of tuples processed at the top level can be found in + * estate->es_processed. + * * We provide a function hook variable that lets loadable plugins * get control when ExecutorRun is called. Such a plugin would * normally call standard_ExecutorRun(). * * ---------------------------------------------------------------- */ -TupleTableSlot * +void ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count) { - TupleTableSlot *result; - if (ExecutorRun_hook) - result = (*ExecutorRun_hook) (queryDesc, direction, count); + (*ExecutorRun_hook) (queryDesc, direction, count); else - result = standard_ExecutorRun(queryDesc, direction, count); - return result; + standard_ExecutorRun(queryDesc, direction, count); } -TupleTableSlot * +void standard_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count) { @@ -247,7 +249,6 @@ standard_ExecutorRun(QueryDesc *queryDesc, CmdType operation; DestReceiver *dest; bool sendTuples; - TupleTableSlot *result; MemoryContext oldcontext; /* sanity checks */ @@ -283,15 +284,13 @@ standard_ExecutorRun(QueryDesc *queryDesc, /* * run plan */ - if (ScanDirectionIsNoMovement(direction)) - result = NULL; - else - result = ExecutePlan(estate, - queryDesc->planstate, - operation, - count, - direction, - dest); + if (!ScanDirectionIsNoMovement(direction)) + ExecutePlan(estate, + queryDesc->planstate, + operation, + count, + direction, + dest); /* * shutdown tuple receiver, if we started it @@ -300,8 +299,6 @@ standard_ExecutorRun(QueryDesc *queryDesc, (*dest->rShutdown) (dest); MemoryContextSwitchTo(oldcontext); - - return result; } /* ---------------------------------------------------------------- @@ -1271,19 +1268,16 @@ ExecEndPlan(PlanState *planstate, EState *estate) /* ---------------------------------------------------------------- * ExecutePlan * - * processes the query plan to retrieve 'numberTuples' tuples in the - * direction specified. + * Processes the query plan until we have processed 'numberTuples' tuples, + * moving in the specified direction. * - * Retrieves all tuples if numberTuples is 0 - * - * result is either a slot containing the last tuple in the case - * of a SELECT or NULL otherwise. + * Runs to completion if numberTuples is 0 * * Note: the ctid attribute is a 'junk' attribute that is removed before the * user can see it * ---------------------------------------------------------------- */ -static TupleTableSlot * +static void ExecutePlan(EState *estate, PlanState *planstate, CmdType operation, @@ -1297,13 +1291,11 @@ ExecutePlan(EState *estate, ItemPointer tupleid = NULL; ItemPointerData tuple_ctid; long current_tuple_count; - TupleTableSlot *result; /* * initialize local variables */ current_tuple_count = 0; - result = NULL; /* * Set the direction. @@ -1332,7 +1324,6 @@ ExecutePlan(EState *estate, /* * Loop until we've processed the proper number of tuples from the plan. */ - for (;;) { /* Reset the per-output-tuple exprcontext */ @@ -1353,13 +1344,10 @@ lnext: ; /* * if the tuple is null, then we assume there is nothing more to - * process so we just return null... + * process so we just end the loop... */ if (TupIsNull(planSlot)) - { - result = NULL; break; - } slot = planSlot; /* @@ -1453,7 +1441,6 @@ lnext: ; default: elog(ERROR, "unrecognized heap_lock_tuple status: %u", test); - return NULL; } } } @@ -1488,35 +1475,30 @@ lnext: ; /* * now that we have a tuple, do the appropriate thing with it.. either - * return it to the user, add it to a relation someplace, delete it - * from a relation, or modify some of its attributes. + * send it to the output destination, add it to a relation someplace, + * delete it from a relation, or modify some of its attributes. */ switch (operation) { case CMD_SELECT: ExecSelect(slot, dest, estate); - result = slot; break; case CMD_INSERT: ExecInsert(slot, tupleid, planSlot, dest, estate); - result = NULL; break; case CMD_DELETE: ExecDelete(tupleid, planSlot, dest, estate); - result = NULL; break; case CMD_UPDATE: ExecUpdate(slot, tupleid, planSlot, dest, estate); - result = NULL; break; default: elog(ERROR, "unrecognized operation code: %d", (int) operation); - result = NULL; break; } @@ -1548,12 +1530,6 @@ lnext: ; /* do nothing */ break; } - - /* - * here, result is either a slot containing a tuple in the case of a - * SELECT or NULL otherwise. - */ - return result; } /* ---------------------------------------------------------------- diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index a58703111c..b34b90428e 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.127 2008/10/31 19:37:56 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.128 2008/10/31 21:07:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -105,8 +105,7 @@ static execution_state *init_execution_state(List *queryTree_list, bool lazyEvalOK); static void init_sql_fcache(FmgrInfo *finfo, bool lazyEvalOK); static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache); -static TupleTableSlot *postquel_getnext(execution_state *es, - SQLFunctionCachePtr fcache); +static bool postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache); static void postquel_end(execution_state *es); static void postquel_sub_params(SQLFunctionCachePtr fcache, FunctionCallInfo fcinfo); @@ -441,10 +440,11 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache) } /* Run one execution_state; either to completion or to first result row */ -static TupleTableSlot * +/* Returns true if we ran to completion */ +static bool postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache) { - TupleTableSlot *result; + bool result; /* Make our snapshot the active one for any called functions */ PushActiveSnapshot(es->qd->snapshot); @@ -460,14 +460,20 @@ postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache) false, /* not top level */ es->qd->dest, NULL); - result = NULL; + result = true; /* never stops early */ } else { /* Run regular commands to completion unless lazyEval */ long count = (es->lazyEval) ? 1L : 0L; - result = ExecutorRun(es->qd, ForwardScanDirection, count); + ExecutorRun(es->qd, ForwardScanDirection, count); + + /* + * If we requested run to completion OR there was no tuple returned, + * command must be complete. + */ + result = (count == 0L || es->qd->estate->es_processed == 0); } PopActiveSnapshot(); @@ -678,22 +684,22 @@ fmgr_sql(PG_FUNCTION_ARGS) */ while (es) { - TupleTableSlot *slot; + bool completed; if (es->status == F_EXEC_START) postquel_start(es, fcache); - slot = postquel_getnext(es, fcache); + completed = postquel_getnext(es, fcache); /* * If we ran the command to completion, we can shut it down now. * Any row(s) we need to return are safely stashed in the tuplestore, * and we want to be sure that, for example, AFTER triggers get fired * before we return anything. Also, if the function doesn't return - * set, we can shut it down anyway because we don't care about - * fetching any more result rows. + * set, we can shut it down anyway because it must be a SELECT and + * we don't care about fetching any more result rows. */ - if (TupIsNull(slot) || !fcache->returnsSet) + if (completed || !fcache->returnsSet) postquel_end(es); /* diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index ac1b81cd9b..2fdaddda4d 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.151 2008/10/29 00:00:39 tgl Exp $ + * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.152 2008/10/31 21:07:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -61,9 +61,9 @@ /* Hook for plugins to get control in ExecutorRun() */ -typedef TupleTableSlot *(*ExecutorRun_hook_type) (QueryDesc *queryDesc, - ScanDirection direction, - long count); +typedef void (*ExecutorRun_hook_type) (QueryDesc *queryDesc, + ScanDirection direction, + long count); extern PGDLLIMPORT ExecutorRun_hook_type ExecutorRun_hook; @@ -140,10 +140,10 @@ extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot); * prototypes from functions in execMain.c */ extern void ExecutorStart(QueryDesc *queryDesc, int eflags); -extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, - ScanDirection direction, long count); -extern TupleTableSlot *standard_ExecutorRun(QueryDesc *queryDesc, - ScanDirection direction, long count); +extern void ExecutorRun(QueryDesc *queryDesc, + ScanDirection direction, long count); +extern void standard_ExecutorRun(QueryDesc *queryDesc, + ScanDirection direction, long count); extern void ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo,