49.6. 执行器
执行器接手规划器/优化器创建的计划,并递归地处理之以抽取所需的行集。这本质上是一种需求拉动的管道机制。每次一个计划节点被调用时,它必须交付一个或多个行,或者报告已经完成了行的交付。
为了提供一个具体例子,假设顶层节点是一个MergeJoin节点。在归并完成之前,两个行必须先被获取(每一个来自于一个子计划)。因此执行器递归地调用它自己去处理子计划(从附加在lefttree的子计划开始)。新的顶层节点(左子计划的顶层节点),我们说是一个Sort节点,并且又要递归来获取一个输入行。Sort的子节点可以是一个SeqScan节点,表示真正地读取一个表。该节点的执行将会使执行器从表中获取一行并将它返回给调用节点。Sort节点将反复调用它的子节点来获得所有需要排序的行。当输入耗尽后(子节点将返回一个NULL来标识),Sort节点执行排序,并且最后能够返回它的第一个输出行,及排序后的第一个。它会把剩下的行保存下来,这样它可以根据后续的要求按照排好的顺序返回这些行。
MergeJoin节点也会相似地从其右子计划要求第一个行。然后它会比较两个子节点提供的行看它们是否能被连接,如果可以它会返回一个连接行给调用者。在下一次调用时,或者它无法连接当前的输入对时,它会前进到一个表或另一个表的下一行(取决于比较的结果),并再次检查匹配。最后,某个子计划耗尽,MergeJoin节点返回NULL表示它没有更多连接行可以提供。
复杂的查询可能涉及多层计划节点,但是一般的方法是相同的:每个节点在被调用时计算并返回它的下一个输出行。每个节点同时也负责应用由规划器分配给它的选择或投影表达式。
执行器机制被用于四种基本SQL查询类型:SELECT、INSERT、 UPDATE以及DELETE。对于SELECT,顶层执行器代码只需要发送查询计划树返回的每个行给客户端。对于INSERT,每一个被返回的行被插入到INSERT中指定的目标表中。这通过一个被称为ModifyTable的特殊顶层计划节点完成(一个简单的INSERT ... VALUES命令会创建一个由一个Result节点组成的简单计划树,该节点只计算一个结果行,在它之上的ModifyTable节点会执行插入。但是INSERT ... SELECT可以用到执行器机制的全部功能)。对于UPDATE,规划器会安排每一个计算行包含所有被更新的列值加上原始目标行的TID(元组ID或行ID),这些数据也会被输入到一个ModifyTable节点,该节点将利用这些信息创建一个新的被更新行并标记旧行为被删除。对于DELETE,唯一被计划返回的列是TID,ModifyTable节点简单地使用TID访问每一个目标行并将其标记为被删除。