You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 17:45:19