diff --git a/catalogs/catalog-jdbc-postgresql/src/main/java/org/apache/gravitino/catalog/postgresql/converter/PostgreSqlTypeConverter.java b/catalogs/catalog-jdbc-postgresql/src/main/java/org/apache/gravitino/catalog/postgresql/converter/PostgreSqlTypeConverter.java index a50e12baf0a..7eb940e483f 100644 --- a/catalogs/catalog-jdbc-postgresql/src/main/java/org/apache/gravitino/catalog/postgresql/converter/PostgreSqlTypeConverter.java +++ b/catalogs/catalog-jdbc-postgresql/src/main/java/org/apache/gravitino/catalog/postgresql/converter/PostgreSqlTypeConverter.java @@ -76,7 +76,23 @@ public Type toGravitino(JdbcTypeBean typeBean) { .map(Types.TimestampType::withTimeZone) .orElseGet(Types.TimestampType::withTimeZone); case NUMERIC: - return Types.DecimalType.of(typeBean.getColumnSize(), typeBean.getScale()); + Integer columnSize = typeBean.getColumnSize(); + Integer scale = typeBean.getScale(); + // Unconstrained NUMERIC (no precision/scale) — PostgreSQL JDBC returns columnSize=0. + // Use ExternalType to preserve arbitrary-precision semantics (backed by BigDecimal). + if (columnSize == null || columnSize == 0) { + return Types.ExternalType.of(NUMERIC); + } + int effectiveScale = scale != null ? scale : 0; + // NUMERIC(P, 0) with small P can be represented as integer types for better performance. + if (effectiveScale == 0) { + if (columnSize < 10) { + return Types.IntegerType.get(); + } else if (columnSize < 19) { + return Types.LongType.get(); + } + } + return Types.DecimalType.of(columnSize, effectiveScale); case VARCHAR: return typeBean.getColumnSize() == null ? Types.StringType.get() diff --git a/catalogs/catalog-jdbc-postgresql/src/test/java/org/apache/gravitino/catalog/postgresql/converter/TestPostgreSqlTypeConverter.java b/catalogs/catalog-jdbc-postgresql/src/test/java/org/apache/gravitino/catalog/postgresql/converter/TestPostgreSqlTypeConverter.java index d976336fb1d..e7ff9d79b4c 100644 --- a/catalogs/catalog-jdbc-postgresql/src/test/java/org/apache/gravitino/catalog/postgresql/converter/TestPostgreSqlTypeConverter.java +++ b/catalogs/catalog-jdbc-postgresql/src/test/java/org/apache/gravitino/catalog/postgresql/converter/TestPostgreSqlTypeConverter.java @@ -68,6 +68,14 @@ public void testToGravitinoType() { checkJdbcTypeToGravitinoType(Types.TimestampType.withoutTimeZone(3), TIMESTAMP, 23, null, 3); checkJdbcTypeToGravitinoType(Types.TimestampType.withoutTimeZone(6), TIMESTAMP, 26, null, 6); checkJdbcTypeToGravitinoType(Types.DecimalType.of(10, 2), NUMERIC, 10, 2, 0); + // Unconstrained NUMERIC (no precision) returns columnSize=0 from JDBC metadata; + // mapped to ExternalType to preserve arbitrary-precision semantics (BigDecimal). + checkJdbcTypeToGravitinoType(Types.ExternalType.of(NUMERIC), NUMERIC, 0, 0, 0); + checkJdbcTypeToGravitinoType(Types.ExternalType.of(NUMERIC), NUMERIC, null, null, 0); + // NUMERIC(P, 0) with small P is mapped to integer types for better performance. + checkJdbcTypeToGravitinoType(Types.IntegerType.get(), NUMERIC, 9, 0, 0); + checkJdbcTypeToGravitinoType(Types.LongType.get(), NUMERIC, 18, 0, 0); + checkJdbcTypeToGravitinoType(Types.DecimalType.of(20, 0), NUMERIC, 20, 0, 0); checkJdbcTypeToGravitinoType(Types.VarCharType.of(20), VARCHAR, 20, null, 0); checkJdbcTypeToGravitinoType(Types.FixedCharType.of(20), BPCHAR, 20, null, 0); checkJdbcTypeToGravitinoType(Types.StringType.get(), TEXT, null, null, 0);