一. 背景
本文通过调试“SELECT * FROM (SELECT custkey, orderkey, rank() OVER (PARTITION BY custkey ORDER BY orderdate ASC FROM orders ) WHERE custkey > 100” 在PredicatePushDown中的执行流程来理解在Presto中谓词下推是如何实现的。该SQL优化前的执行计划树如下:
二. PredicatePushDown的谓词下推实现
step1:
首先PredicatePushDown会访问顶层节点OutputNode,因为PredicatePushDown::Rewriter没有实现visitOutputNode方法,因此调用了visitPlan方法。visitOutputNode优化之前的计划执行树如下:
step2:
step1中的visitPlan的context.defaultRewrite方法,会递归rewrite OutputNode的source节点,因此访问OutPutNode之前,会先调用了PredicatePushDown:Rewriter::visitFilter访问FilterNode,viistFilterNode优化之前的计划执行树:
step3:
step2中的visitFilter的context.rewrite方法,会递归rewrite FilterNode的source节点,因此访问FilterNode之前,先调用了PredicatePushDown:Rewriter::visitProject访问ProjectNode,visitProjectNode优化前的计划执行树:
step4:
step3中的visitProject的context.defaultRewrite方法,会递归rewrite ProjectNode的source节点,因此访问ProjectNode之前,先调用了PredicatePushDown:Rewriter::visitWindow访问WindowNode,visitWindowNode优化前的计划执行树:
step5:
step4中的visitWindow的context.defaultRewrite方法,会递归rewrite WindowNode的source节点,因此访问WindowNode之前,先调用了PredicatePushDown:Rewriter::visitTableScan访问TableScanNode,visitTableScanNode优化前的计划执行树:
step6:
visitTableScan的时候通过如下代码插入了一个FilterNode,实现了Filter下推:
public PlanNode visitTableScan(TableScanNode node, RewriteContext<Expression> context)
{
Expression predicate = simplifyExpression(context.get());
if (!TRUE_LITERAL.equals(predicate)) {
return new FilterNode(idAllocator.getNextId(), node, predicate);
}
return node;
}
step7:
visitTableScan优化后的执行计划树:
step8:
递归返回step4的visitWindow,visitWindow会用优化后的source替换优化前的source节点,替换优化后source后的执行计划树:
step9:
递归返回step3的visitProject,visitProject会用优化后的source替换优化前的source节点,替换优化后source后的执行计划树:
step10:
递归返回step2的visitFilter,visitFilter中发现优化后,直接用sourse节点替换原来的执行计划树,因为visitFilter后的执行计划树:
step11:
递归返回step1的visitOutput,visitOutput会用优化后的source替换优化前的source节点,替换优化后source后的执行计划树:
step12:
因此,经过PredicatePushDown优化后的计划执行树如下所示,实现了谓词下推。