You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何强制Calcite执行RelNode时不优化SQL查询?

Calcite忽略RelNode操作直接下推SELECT *的解决办法

问题场景

在构建并执行RelNode时,仅连接单个JDBC数据源到Calcite,但Calcite会忽略RelNode中定义的聚合、列重命名、排序等操作,直接向数据源下发SELECT *语句。这是因为Calcite优化引擎判定全量拉取数据到应用端处理成本更低,但实际需求是让Calcite严格按照RelNode的定义执行下推。

代码示例

Class.forName(CALCITE_JDBC_DRIVER);
Properties info = new Properties();
info.setProperty("lex", "JAVA");
Connection connection = DriverManager.getConnection("jdbc:calcite:", info);
calciteConnection = connection.unwrap(CalciteConnection.class);

rootSchema = calciteConnection.getRootSchema();
rootSchema.add("foo", JdbcSchema...);

frameworkConfig = Frameworks.newConfigBuilder()
        .parserConfig(SqlParser.Config.DEFAULT)
        .defaultSchema(rootSchema)
        .programs()
        .traitDefs(ConventionTraitDef.INSTANCE, RelDistributionTraitDef.INSTANCE)
        .operatorTable(SqlOperatorTables.chain(
                TrinoSqlOperatorTable.INSTANCE,
                SqlStdOperatorTable.instance()))
        .build();

relRunner = calciteConnection.unwrap(RelRunner.class);

RelBuilder relBuilder = RelBuilder.create(frameworkConfig);
var relNode = relBuilder
        .scan("foo", "bar", "table")
        .rename(relBuilder.peek().getRowType().getFieldNames().stream()
                .map(f -> f + "_postfix")
                .toList())
        .sort(relBuilder.field(1))
        .aggregate(relBuilder.groupKey(2),
                relBuilder.sum(relBuilder.field(3)))
        .build();
relRunner.prepareStatement(relNode).executeQuery();

实际下推的SQL

SELECT
  *
FROM
  "bar"."table"

解决思路

1. 禁用或精简优化规则

在构建FrameworkConfig时,指定空的优化程序,完全跳过Calcite的优化逻辑,让RelNode按原始定义执行:

frameworkConfig = Frameworks.newConfigBuilder()
        .parserConfig(SqlParser.Config.DEFAULT)
        .defaultSchema(rootSchema)
        // 配置空规则集,禁用所有优化
        .programs(Programs.of(Programs.RULE_SET.empty()))
        .traitDefs(ConventionTraitDef.INSTANCE, RelDistributionTraitDef.INSTANCE)
        .operatorTable(SqlOperatorTables.chain(
                TrinoSqlOperatorTable.INSTANCE,
                SqlStdOperatorTable.instance()))
        .build();

如果需要保留部分必要规则(比如JDBC数据源的适配规则),可以手动筛选规则,排除会将操作上移到应用端的规则(如AggregateRemoveRule、ProjectRemoveRule等)。

2. 强制指定JDBC约定(Convention)

通过Planner将RelNode的TraitSet替换为JdbcConvention,强制Calcite将所有操作下推到JDBC数据源执行:

RelOptPlanner planner = frameworkConfig.getPlanner();
// 目标Trait:使用JDBC约定
RelTraitSet desiredTraits = relNode.getTraitSet().replace(JdbcConvention.INSTANCE);
RelNode optimizedRel = planner.changeTraits(relNode, desiredTraits);
planner.setRoot(optimizedRel);
// 获取最终的可下推RelNode
RelNode finalRel = planner.findBestExp();

// 使用finalRel执行查询
relRunner.prepareStatement(finalRel).executeQuery();

3. 配置JDBCSchema的下推参数

创建JdbcSchema时,禁用自动下推优化,或者限制下推的操作类型:

// 创建JDBCSchema时禁用自动下推
JdbcSchema jdbcSchema = JdbcSchema.builder(rootSchema, "foo")
        .dataSource(dataSource)
        .defaultSchema("bar")
        .autoPushDown(false) // 关闭自动下推优化
        .build();
rootSchema.add("foo", jdbcSchema);

4. 使用RelRunner的直接执行模式

部分Calcite版本中,RelRunner的run方法可以直接执行原始RelNode,跳过优化流程:

// 直接执行原始RelNode,不经过优化
relRunner.run(relNode);

内容的提问来源于stack exchange,提问作者kalmar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 12:55:14