如何在Apache Calcite中验证单独表达式?遇列未找到问题求助
问题解决:Apache Calcite单独验证条件表达式时列找不到的异常
问题原因
验证完整SQL查询时,parseQuery()解析出的SqlSelect包含FROM子句,Calcite的SqlValidator会自动从FROM子句中获取表的行类型作为WHERE子句的验证上下文;而验证孤立条件表达式时,parseExpression()仅得到独立的表达式节点(如SqlBinaryOp),Validator没有默认上下文来识别列名,因此抛出Column 'a' not found in any table异常。
解决方案
手动为SqlValidator指定验证上下文,明确表达式基于目标表的行类型进行验证。具体实现步骤如下:
- 在初始化阶段获取目标表的Calcite表条目,创建对应的
SqlValidatorNamespace - 从
SqlValidatorNamespace中提取表的SqlValidatorScope作为验证上下文 - 调用带范围参数的
validate方法,传入表达式和上下文完成验证
修改后的代码
ConditionValidator类
public class ConditionValidator { private final String sql; private final SchemaPlus rootSchema; private final FrameworkConfig frameworkConfig; private final RelDataTypeFactory relDataTypeFactory; private final CalciteCatalogReader catalogReader; private final SqlOperatorTable sqlOperatorTable; private final SqlValidator validator; private final SqlValidatorScope tableScope; // 目标表的验证范围 public ConditionValidator(String sql) { this.sql = sql; SchemaPlus rootSchema = Frameworks.createRootSchema(true); DummyTable testTable = new DummyTable("test_table"); rootSchema.add(testTable.getTableName(), testTable); this.rootSchema = rootSchema; this.frameworkConfig = Frameworks.newConfigBuilder() .parserConfig(SqlParser.config() .withLex(Lex.MYSQL) .withConformance(SqlConformanceEnum.DEFAULT)) .defaultSchema(rootSchema) .operatorTable(SqlStdOperatorTable.instance()) .build(); this.relDataTypeFactory = new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT); Properties properties = new Properties(); properties.setProperty(CalciteConnectionProperty.CASE_SENSITIVE.camelName(), "true"); this.catalogReader = new CalciteCatalogReader( CalciteSchema.from(rootSchema), CalciteSchema.from(rootSchema).path(rootSchema.getName()), relDataTypeFactory, new CalciteConnectionConfigImpl(properties)); this.sqlOperatorTable = SqlOperatorTables.chain(frameworkConfig.getOperatorTable(), catalogReader); this.validator = SqlValidatorUtil.newValidator(sqlOperatorTable, catalogReader, relDataTypeFactory, frameworkConfig.getSqlValidatorConfig()); // 初始化目标表的验证范围 CalciteSchema schema = CalciteSchema.from(rootSchema); CalciteSchema.TableEntry tableEntry = schema.getTable(testTable.getTableName(), true); SqlValidatorNamespace tableNamespace = validator.createNamespace(tableEntry, null); tableNamespace.validate(); this.tableScope = validator.getNamespaceScope(tableNamespace); } public SqlNode validate() { SqlParser sqlParser = SqlParser.create(sql, frameworkConfig.getParserConfig()); SqlNode sqlNode; try { sqlNode = sqlParser.parseExpression(); } catch (Exception e) { throw new RuntimeException(e); } // 传入表的验证范围,让Validator识别表达式中的列 return validator.validate(sqlNode, tableScope); } }
DummyTable类(保持不变)
public class DummyTable extends AbstractTable { private final String tableName; public DummyTable(String tableName) { this.tableName = tableName; } @Override public RelDataType getRowType(RelDataTypeFactory typeFactory) { RelDataTypeFactory.Builder builder = typeFactory.builder(); builder.add("a", SqlTypeName.BIGINT); builder.add("b", SqlTypeName.BIGINT); return builder.build(); } public String getTableName() { return tableName; } }
代码说明
- 新增
tableScope字段存储目标表的验证上下文,确保Validator知道表达式中的列来自哪个表 - 在构造函数中通过CalciteSchema获取表条目,创建并验证
SqlValidatorNamespace,进而得到对应的验证范围 - 验证表达式时调用
validator.validate(sqlNode, tableScope),传入上下文后Validator即可正确识别列名并验证表达式合法性
内容的提问来源于stack exchange,提问作者Kyabia
相关产品推荐
相关产品推荐

