Apache Calcite转换SQL添加ASYMMETRIC致Snowflake执行报错的解决办法
解决Apache Calcite 1.34.0转换ANSI SQL到Snowflake时BETWEEN关键字的ASYMMETRIC问题
问题原因
你设置了SqlConformanceEnum.STRICT_99的解析一致性,这会强制Calcite遵循ANSI SQL 99标准——该标准中BETWEEN默认是ASYMMETRIC语义,因此解析后的SqlNode会显式带上这个关键字。但Snowflake SQL不支持ASYMMETRIC修饰符,导致编译报错。
可行解决办法
1. 调整解析器的一致性配置(推荐)
直接将解析器的一致性改为Snowflake兼容模式,Calcite会自动适配Snowflake的语法规则,不会生成ASYMMETRIC关键字:
SqlParser sqlParser = SqlParser.create(ansiSql, SqlParser.configBuilder() .setConfig(SqlParser.Config.DEFAULT) .setLex(Lex.MYSQL_ANSI) .setConformance(SqlConformanceEnum.SNOWFLAKE) // 替换为SNOWFLAKE兼容模式 .build());
如果Snowflake模式仍有其他适配问题,也可以尝试SqlConformanceEnum.LENIENT宽松模式,它会尽可能兼容多种数据库语法。
2. 自定义Snowflake Dialect,重写BETWEEN解析逻辑
如果必须保留STRICT_99的解析规则,可以通过继承SnowflakeDialect,重写unparseBetween方法来去掉ASYMMETRIC关键字:
public class CustomSnowflakeDialect extends SnowflakeSqlDialect { public CustomSnowflakeDialect(Context context) { super(context); } @Override public void unparseBetween(SqlWriter writer, SqlCall call, boolean isNot) { // 直接输出BETWEEN,不添加ASYMMETRIC/SYMMETRIC修饰符 writer.keyword(isNot ? "NOT BETWEEN" : "BETWEEN"); call.operand(1).unparse(writer, 0, 0); writer.keyword("AND"); call.operand(2).unparse(writer, 0, 0); } }
然后使用自定义Dialect生成SQL:
SqlDialect snowflakeDialect = new CustomSnowflakeDialect( SqlDialect.EMPTY_CONTEXT.withDatabaseProduct(SqlDialect.DatabaseProduct.SNOWFLAKE)); String snowflakeSql = sqlNode.toSqlString(snowflakeDialect).getSql();
3. 字符串替换(临时 workaround)
如果以上两种方法都无法快速实施,可以在生成Snowflake SQL后,直接做字符串替换:
String snowflakeSql = sqlNode.toSqlString(snowflakeDialect).getSql(); // 替换掉多余的ASYMMETRIC关键字 snowflakeSql = snowflakeSql.replace("BETWEEN ASYMMETRIC", "BETWEEN"); snowflakeSql = snowflakeSql.replace("NOT BETWEEN ASYMMETRIC", "NOT BETWEEN"); executeQuery(snowflakeSql);
注意:这种方式属于硬编码替换,若后续Calcite生成的语法有变化,可能失效,仅适合临时救急。
内容的提问来源于stack exchange,提问作者konoha.itachi
相关产品推荐
相关产品推荐

