Apache Calcite连接PostgreSQL遇DECIMAL精度0异常问题咨询
问题解答
1. 这是否属于Apache Calcite的Bug?
属于Calcite的兼容性Bug。PostgreSQL允许定义无精度、刻度的NUMERIC类型(等价于无参数的NUMERIC,实际存储支持任意精度),但PostgreSQL JDBC驱动返回这类列的元数据时,会将精度和刻度都返回为0。而Calcite的SqlTypeFactoryImpl#createSqlType方法对DECIMAL类型有严格校验,要求精度必须在1-19之间,未处理精度为0的兼容场景,因此触发异常。
2. 可行的解决办法
方案一:修改PostgreSQL表结构(最简单直接)
显式为NUMERIC列指定精度和刻度,让JDBC驱动返回合法的元数据,避免Calcite校验失败:
ALTER TABLE test_table ALTER COLUMN value TYPE NUMERIC(19, 2); -- 可根据业务需求调整精度和刻度
方案二:自定义Calcite类型工厂,拦截处理精度为0的DECIMAL类型
重写SqlTypeFactoryImpl的createSqlType方法,当遇到精度为0的DECIMAL类型时,替换为Calcite支持的默认精度(比如PostgreSQL常用的最大兼容精度19,刻度0):
import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.type.SqlTypeFactoryImpl; import org.apache.calcite.sql.type.RelDataTypeSystem; public class PgCompatibleSqlTypeFactory extends SqlTypeFactoryImpl { public PgCompatibleSqlTypeFactory(RelDataTypeSystem typeSystem) { super(typeSystem); } @Override public RelDataType createSqlType(SqlTypeName typeName, int precision, int scale) { // 处理PostgreSQL无精度NUMERIC返回的精度0场景 if (typeName == SqlTypeName.DECIMAL && precision == 0) { // 替换为Calcite支持的最大精度19,刻度可根据业务调整 return super.createSqlType(SqlTypeName.DECIMAL, 19, 0); } return super.createSqlType(typeName, precision, scale); } }
在创建Calcite连接时指定自定义类型工厂:
CalciteConnection calciteConnection = DriverManager.getConnection("jdbc:calcite:") .unwrap(CalciteConnection.class); // 设置自定义类型工厂 calciteConnection.setTypeFactory( new PgCompatibleSqlTypeFactory(calciteConnection.getTypeFactory().getTypeSystem()) ); // 后续初始化JdbcSchema等操作
方案三:尝试升级Apache Calcite版本
Calcite社区可能已在后续版本修复了该PostgreSQL兼容性问题,可尝试升级到1.40.0及以上版本,验证是否解决异常。
方案四:自定义JdbcSchema的元数据处理器
通过实现JdbcSchema.MetadataHandler接口,拦截获取列元数据的逻辑,将精度为0的NUMERIC列的精度替换为合法值:
import java.sql.SQLException; import java.util.List; import java.util.stream.Collectors; import org.apache.calcite.schema.impl.JdbcSchema; // 自定义元数据处理器,处理列元数据 JdbcSchema.MetadataHandler metadataHandler = new JdbcSchema.MetadataHandler() { @Override public List<JdbcSchema.Column> getColumns(JdbcSchema schema, String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException { List<JdbcSchema.Column> columns = JdbcSchema.MetadataHandler.super.getColumns(schema, catalog, schemaPattern, tableNamePattern, columnNamePattern); // 遍历列,修正NUMERIC类型的精度 return columns.stream() .map(column -> { if ("NUMERIC".equals(column.typeName) && column.precision == 0) { return new JdbcSchema.Column( column.catalog, column.schema, column.table, column.name, column.position, column.typeName, 19, // 替换为合法精度 0, // 替换为合法刻度 column.nullable, column.remarks, column.columnDef, column.sqlDataType, column.sqlDataTypeName, column.charOctetLength, column.isGeneratedColumn ); } return column; }) .collect(Collectors.toList()); } }; // 创建JdbcSchema时指定自定义元数据处理器 JdbcSchema.create( rootSchema, "pgSchema", dataSource, metadataHandler, "public" );
内容的提问来源于stack exchange,提问作者Eric
相关产品推荐
相关产品推荐

