接前面:
/* ---------------------------------------------------------------- * ExecutorRun * * This is the main routine of the executor module. It accepts * the query descriptor from the traffic cop and executes the * query plan. * * ExecutorStart must have been called already. * * If direction is NoMovementScanDirection then nothing is done * except to start up/shut down the destination. Otherwise, * we retrieve up to 'count' tuples in the specified direction. * * 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(). * * ---------------------------------------------------------------- */ void ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count) { if (ExecutorRun_hook) (*ExecutorRun_hook) (queryDesc, direction, count); else standard_ExecutorRun(queryDesc, direction, count); } void standard_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count) { EState *estate; CmdType operation; DestReceiver *dest; bool sendTuples; MemoryContext oldcontext; /* sanity checks */ Assert(queryDesc != NULL); 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 Executor overall runtime */ if (queryDesc->totaltime) InstrStartNode(queryDesc->totaltime); /* * extract information from the query descriptor and the query feature. */ operation = queryDesc->operation; dest = queryDesc->dest; /* * startup tuple receiver, if we will be emitting tuples */ estate->es_processed = 0; estate->es_lastoid = InvalidOid; sendTuples = (operation == CMD_SELECT || queryDesc->plannedstmt->hasReturning); if (sendTuples) (*dest->rStartup) (dest, operation, queryDesc->tupDesc); /* * run plan */ if (!ScanDirectionIsNoMovement(direction)) ExecutePlan(estate, queryDesc->planstate, operation, sendTuples, count, direction, dest); /* * shutdown tuple receiver, if we started it */ if (sendTuples) (*dest->rShutdown) (dest); if (queryDesc->totaltime) InstrStopNode(queryDesc->totaltime, estate->es_processed); MemoryContextSwitchTo(oldcontext); }
其中,最为核心的,也就是这一段了:
ExecutePlan(estate, queryDesc->planstate, operation, sendTuples, count, direction, dest);
展开 ExecutePlan的源代码:
static void ExecutePlan(EState *estate, PlanState *planstate, CmdType operation, bool sendTuples, long numberTuples, ScanDirection direction, DestReceiver *dest) { TupleTableSlot *slot; long current_tuple_count; /* * initialize local variables */ current_tuple_count = 0; /* * Set the direction. */ estate->es_direction = direction; /* * Loop until we've processed the proper number of tuples from the plan. */ for (;;) { fprintf(stderr,"In ExecutePlan ...for loop...\n"); /* Reset the per-output-tuple exprcontext */ ResetPerTupleExprContext(estate); /* * Execute the plan and obtain a tuple */ slot = ExecProcNode(planstate); /* * if the tuple is null, then we assume there is nothing more to * process so we just end the loop... */ if (TupIsNull(slot)) break; /* * If we have a junk filter, then project a new tuple with the junk * removed. * * Store this new "clean" tuple in the junkfilter's resultSlot. * (Formerly, we stored it back over the "dirty" tuple, which is WRONG * because that tuple slot has the wrong descriptor.) */ if (estate->es_junkFilter != NULL) slot = ExecFilterJunk(estate->es_junkFilter, slot); /* * If we are supposed to send the tuple somewhere, do so. (In * practice, this is probably always the case at this point.) */ if (sendTuples) (*dest->receiveSlot) (slot, dest); /* * Count tuples processed, if this is a SELECT. (For other operation * types, the ModifyTable plan node must count the appropriate * events.) */ if (operation == CMD_SELECT) (estate->es_processed)++; /* * check our tuple count.. if we've processed the proper number then * quit, else loop again and process more tuples. Zero numberTuples * means no limit. */ current_tuple_count++; if (numberTuples && numberTuples == current_tuple_count) break; } }
把它缩略一下,得到的是:
static void ExecutePlan(EState *estate, PlanState *planstate, CmdType operation, bool sendTuples, long numberTuples, ScanDirection direction, DestReceiver *dest) { ... /* * Loop until we've processed the proper number of tuples from the plan. */ for (;;) { ... /* * check our tuple count.. if we've processed the proper number then * quit, else loop again and process more tuples. Zero numberTuples * means no limit. */ current_tuple_count++; if (numberTuples && numberTuples == current_tuple_count) break; } }
可以发现:对于有很多条记录第表tst01而言,select * from tst01 where id<10 ,执行循环10次。
下面,仔细探究其for 循环的内容,从其循环退出条件的角度来看看:
static void ExecutePlan(EState *estate, PlanState *planstate, CmdType operation, bool sendTuples, long numberTuples, ScanDirection direction, DestReceiver *dest) { TupleTableSlot *slot; long current_tuple_count; /* * initialize local variables */ current_tuple_count = 0; ... for (;;) {
...
/* * if the tuple is null, then we assume there is nothing more to * process so we just end the loop... */ if (TupIsNull(slot)) break;
... /* * check our tuple count.. if we've processed the proper number then * quit, else loop again and process more tuples. Zero numberTuples * means no limit. */ current_tuple_count++; if (numberTuples && numberTuples == current_tuple_count) break; } }
可以说,应当查询多少条记录,如果表中有10条记录符合条件,那么执行10循环后,从 if (TupIsNull(slot)) 跳出去。