如何正确序列化含BETWEEN过滤条件的Calcite RelNode?
Calcite RelBuilder构建Filter节点序列化AssertError问题解决
问题描述
我希望通过RelBuilder构建Calcite RelNode并序列化后发送至其他系统。但发现构建Filter节点时,条件会被简化为SEARCH类型的RexCall,其值为Sarg。使用RelJsonWriter序列化该RelNode时触发了AssertError,经排查是Sarg导致的。使用Calcite版本为calcite-core-1.32.0,请问我的使用方式是否有误?
测试代码如下:
@Test public void buildFilterTest() { try { SchemaPlus rootSchema = Frameworks.createRootSchema(true); rootSchema.add("USERS", new AbstractTable() { // note: add a table @Override public RelDataType getRowType(final RelDataTypeFactory typeFactory) { RelDataTypeFactory.Builder builder = typeFactory.builder(); builder.add("ID", new BasicSqlType(new RelDataTypeSystemImpl() {}, SqlTypeName.INTEGER)); builder.add("NAME", new BasicSqlType(new RelDataTypeSystemImpl() {}, SqlTypeName.CHAR)); builder.add("AGE", new BasicSqlType(new RelDataTypeSystemImpl() {}, SqlTypeName.INTEGER)); return builder.build(); } }); final FrameworkConfig config = Frameworks.newConfigBuilder().parserConfig(SqlParser.Config.DEFAULT) .defaultSchema(rootSchema).build(); RelBuilder builder = RelBuilder.create(config); builder.scan("USERS"); builder.project(builder.field("ID")); RexNode greater = builder.call(SqlStdOperatorTable.GREATER_THAN, builder.field("ID"), builder.literal(0)); RexNode lesser = builder.call(SqlStdOperatorTable.LESS_THAN, builder.field("ID"), builder.literal(100)); builder.filter(builder.call(SqlStdOperatorTable.AND, greater, lesser)); RelNode relNode = builder.build(); RelJsonWriter writer = new RelJsonWriter(); relNode.explain(writer); String res = writer.asString(); log.info("success"); } catch (AssertionError e) { e.printStackTrace(); } }
问题原因
Calcite的RelBuilder默认启用了Sarg优化,会将连续的范围条件(如ID>0 AND ID<100)合并为SEARCH类型的RexCall,内部用Sarg对象表示范围。但在1.32.0版本中,RelJsonWriter并未适配Sarg类型的RexNode,序列化时触发断言错误。
解决方案
通过禁用RelBuilder的Sarg优化来规避该问题,具体是在创建FrameworkConfig时,添加RelBuilder.Config并关闭Sarg转换:
修改后的代码如下:
@Test public void buildFilterTest() { try { SchemaPlus rootSchema = Frameworks.createRootSchema(true); rootSchema.add("USERS", new AbstractTable() { // note: add a table @Override public RelDataType getRowType(final RelDataTypeFactory typeFactory) { RelDataTypeFactory.Builder builder = typeFactory.builder(); builder.add("ID", new BasicSqlType(new RelDataTypeSystemImpl() {}, SqlTypeName.INTEGER)); builder.add("NAME", new BasicSqlType(new RelDataTypeSystemImpl() {}, SqlTypeName.CHAR)); builder.add("AGE", new BasicSqlType(new RelDataTypeSystemImpl() {}, SqlTypeName.INTEGER)); return builder.build(); } }); // 新增RelBuilder配置,关闭Sarg转换 RelBuilder.Config relBuilderConfig = RelBuilder.Config.DEFAULT.withConvertToSarg(false); final FrameworkConfig config = Frameworks.newConfigBuilder() .parserConfig(SqlParser.Config.DEFAULT) .defaultSchema(rootSchema) .relBuilderConfig(relBuilderConfig) // 将RelBuilder配置传入 .build(); RelBuilder builder = RelBuilder.create(config); builder.scan("USERS"); builder.project(builder.field("ID")); RexNode greater = builder.call(SqlStdOperatorTable.GREATER_THAN, builder.field("ID"), builder.literal(0)); RexNode lesser = builder.call(SqlStdOperatorTable.LESS_THAN, builder.field("ID"), builder.literal(100)); builder.filter(builder.call(SqlStdOperatorTable.AND, greater, lesser)); RelNode relNode = builder.build(); RelJsonWriter writer = new RelJsonWriter(); relNode.explain(writer); String res = writer.asString(); log.info("success"); } catch (AssertionError e) { e.printStackTrace(); } }
关键修改点
- 创建
RelBuilder.Config实例,调用withConvertToSarg(false)关闭自动将范围条件转换为Sarg的优化 - 将该RelBuilder配置通过
relBuilderConfig()方法传入FrameworkConfig的构建流程中
这样修改后,Filter节点的条件会保持原本的AND连接的GREATER_THAN和LESS_THAN表达式,RelJsonWriter可以正常序列化,不会触发断言错误。
内容的提问来源于stack exchange,提问作者lulijun
相关产品推荐
相关产品推荐

