GSoC 2017中有个改造pipeline的项目,基本思想是遍历执行计划树,找到叶子节点,从叶子节点开始获取数据,然后推送给各个父节点。
执行器中,使用RunNode函数递归调用,得到叶子节点:先遍历右节点,然后再遍历左节点;当然若没有右节点,则直接遍历左节点;当没有左右子节点时,就到了叶子节点,那么通过pushTuple来推送数据。
pushTuple根据父节点类型调用各自推送函数,将数据推送给父节点,比如上面流程:当父节点是LimitState时,调用pushTupleToLimit进行推送。
我们看下SeqScan:其实就是从存储引擎获取数据,进行过滤和投影,然后根据父节点类型,推送给父节点。
pushTupleToSeqScan(SeqScanState *node)
heappushtups(...,node->ss.ps.parent,node)
|-- get a tuple in the page
SeqPushHeapTuple(HeapTuple tuple, PlanState *node,SeqScanState *pusher)
|-- slot = SeqStoreTuple(pusher, tuple);
|-- ExecQual && ExecProject
|-- return pushTuple(slot, node, (PlanState *) pusher);
|-- if (!node){//pusher top level node, send to dest
return SendReadyTuple(slot, pusher);
}
对于hash join来说,需要先构建hash表,然后外表数据从hash表中进行探测;pipeline引擎中怎么推送完成hash join呢?
从RunNode函数中可以也可以看到,他是先从内表分支开始推送数据,推送给Hash节点构建hash表,然后推送给父节点。pushTuple函数中,当hash join的右分支推送上来时,pushTupleToHashJoinFromInner函数仅获取hash表,并不继续向上推送;而是HashJoin的左子分支推送上来的数据进入pushTupleToHashJoinFromOuter,进行hash探测,找到符合条件的数据,并向上层父节点推送join结果:
可以得知,该改造并没有充分利用各个叶子分支并行,未来可以向整个方向进行优化。