在Unresolved LogicalPlan逻辑算子树的操作(如绑定、解析、优化等)中,主要方法都是基于规则(Rule)的,通过Scala语言模式匹配机制(Pattern-match)进行树结构的转换或节点改写。Rule是一个抽象类,子类需要复写apply(plan:TreeType)方法来制定特定的处理逻辑,基本定义如下。
abstract class Rule[TreeType <: TreeNode[_]] extends Logging {
/** Name for this rule, automatically inferred based on class name. */
val ruleName: String = {
val className = getClass.getName
if (className endsWith "$") className.dropRight(1) else className
}
def apply(plan: TreeType): TreeType
}
有了各种具体规则后,还需要驱动程序来调用这些规则,在Catalyst中这个功能由RuleExecutor提供。凡是涉及树型结构的转换过程(如Analyzer逻辑算子树分析过程、Optimizer逻辑算子树的优化过程和后续物理算子树的生成过程等),都要实施规则匹配和节点处理,都继承自RuleExecutor[TreeType]抽象类,如下图所示。
RuleExecutor内部提供了一个Seq[Batch],里面定义的是该RuleExecutor的处理步骤。每个Batch代表一套规则,配备一个策略,该策略说明了迭代次数(一次还是多次)。RuleExecutor的apply(plan:TreeType):TreeType方法会按照batches顺序和batch内的Rules顺序,对传入的plan里的节点进行迭代处理,处理逻辑由具体Rule子类实现。
def execute(plan: TreeType): TreeType = {
var curPlan = plan
batches.foreach { batch =>
val batchStartPlan = curPlan
var iteration = 1
var lastPlan = curPlan
var continue = true
while (continue) {
curPlan = batch.rules.foldLeft(curPlan) {
case (plan, rule) => rule(plan)
}
iteration += 1
if (iteration > batch.strategy.maxIterations) {
continue = false
}
if (curPlan.fastEquals(lastPlan)) {
continue = false
}
lastPlan = curPlan
}
}
curPlan
}