YON Blog

Calcite 手动执行 RelNode

2022.12.06 更新:控制节点的执行顺序也可以使用 EnumerableRel 只是稍微麻烦点,但也挺优雅。

项目里有个需求,需要让 Calcite 里 TableScan 按顺序执行,后执行的 TableScan 依赖前面 TableSacn 执行的返回数据。试了各种方法,要么是因为 Calcite 运行机制导致无法生效,要么就是太不优雅自己无法接受。后来想了个法子就是不依赖 Calcite 框架来运行 TableScan,自己手动运行来控制前后顺序并传递变量。

办法也很简单就是直接用 Interpreter 来执行,不过前提是要运行的节点是 BindableRel(Interpreter 运行节点使用了Nodes.CoreCompiler 不仅支持 BindableRel 还支持框架本身提供的节点)。最终,为了实现自己运行节点,需要我们写一个简单的优化规则,建议是直接对 Logical 的节点优化,这样处理比较方便。然后就是构造一个 BindableRel 把待手动运行的节点作为属性保存起来,最后在 run 方法里调用 Interpreter 运行节点即可。示例代码:

/**
 * @param DataContext 上下文
 * @param RelNode 待手动运行的节点
 */
private List<Object[]> interpret(DataContext dataContext, RelNode root) {
    try(
            Interpreter interpreter = new Interpreter(dataContext, root);
    ) {
        List<Object[]> result = new ArrayList<>();
        Enumerator<Object[]> enumerator = interpreter.enumerator();
        while (enumerator.moveNext()) {
            result.add(enumerator.current());
        }
        return result;
    }
}