execMain.c 的 ExecutePlan 函数
首先看他的注释
ExecutePlan
Processes the query plan until we have retrieved 'numberTuples' tuples, moving in the specified direction.
Runs to completion if numberTuples is 0
Note: the ctid attribute is a 'junk' attribute that is removed before the user can see it
直接翻译为
执行计划
处理查询计划,直到我们检索到'numberTuples'图元,按指定的方向移动。
如果numberTuples为0,则运行至完成。
注意:ctid属性是一个 "垃圾 "属性,在用户看到它之前会被删除。
首先初始化本地变量
current_tuple_count = 0;
设置direction
estate->es_direction = direction;
如果计划有可能被多次执行,则必须强制它在没有并行的情况下运行,因为我们可能会提前退出。
if (!execute_once)
use_parallel_mode = false;
estate->es_use_parallel_mode = use_parallel_mode;
if (use_parallel_mode)
EnterParallelMode();
循环直到我们处理完计划中正确数量的tuples。
for (;;)
{
/* 重置每个 输出单元 的exprcontext */
ResetPerTupleExprContext(estate);
/* 执行计划并获得一个元组 */
slot = ExecProcNode(planstate);
/*如果元组为空,那么我们就认为没有什么可处理的了,所以我们就结束循环*/
if (TupIsNull(slot))
break;
/* 如果我们有一个垃圾过滤器,那么就投射一个新的元组,把垃圾去掉。
将这个新的 "干净 "元组存储在junkfilter的 resultSlot中。
(以前,我们把它存储在 "脏 "元组上面,这是错误的,因为那个元组槽的描述符是错误的)。*/
if (estate->es_junkFilter != NULL)
slot = ExecFilterJunk(estate->es_junkFilter, slot);
/* 如果我们应该把元组发送到某个地方,就这样做。
(在实践中,在这一点上可能总是这样的)。*/
if (sendTuples)
{
/*如果我们不能发送元组,我们就认为目的地已经关闭,不能再发送元组。
如果是这样,就结束这个循环。 */
if (!dest->receiveSlot(slot, dest))
break;
}
/* 如果这是一个SELECT,则计算所处理的图元。
(对于其他操作类型,ModifyTable计划节点必须计算适当的事件。)
*/
if (operation == CMD_SELECT)
(estate->es_processed)++;
/* 检查我们的元组数量。如果我们已经处理了适当的数量,那么就退出,
否则就再次循环,处理更多的元组。
零图元数意味着没有限制。
*/
current_tuple_count++;
if (numberTuples && numberTuples == current_tuple_count)
break;
}
循环结束
// 如果我们知道我们不需要备份,我们可以在这个时候释放资源。
if (!(estate->es_top_eflags & EXEC_FLAG_BACKWARD))
(void) ExecShutdownNode(planstate);
if (use_parallel_mode)
ExitParallelMode();