如何强制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
相关产品推荐
相关产品推荐

