From 9c965e0c7c94ca3a835fadbc9554daff9e259c78 Mon Sep 17 00:00:00 2001 From: Nevin Zheng Date: Sat, 18 Jul 2026 06:45:26 -0700 Subject: [PATCH 1/5] fix(oceanbase): reject nanosecond timestamps --- .../converter/OceanBaseTypeConverter.java | 9 +++++ .../converter/TestOceanBaseTypeConverter.java | 19 +++++++++ .../integration/test/CatalogOceanBaseIT.java | 39 +++++++++++++++++++ ...OceanBaseTableOperationsSqlGeneration.java | 22 +++++++++++ docs/jdbc-oceanbase-catalog.md | 9 +++++ 5 files changed, 98 insertions(+) diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java index fe5a43fa4b9..e44396e0801 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java @@ -26,6 +26,8 @@ /** Type converter for OceanBase. */ public class OceanBaseTypeConverter extends JdbcTypeConverter { + private static final int MAX_TIMESTAMP_PRECISION = 6; + static final String TINYINT = "tinyint"; static final String TINYINT_UNSIGNED = "tinyint unsigned"; static final String SMALLINT = "smallint"; @@ -137,6 +139,13 @@ public String fromGravitino(Type type) { return type.simpleString(); } else if (type instanceof Types.TimestampType) { Types.TimestampType timestampType = (Types.TimestampType) type; + if (timestampType.hasPrecisionSet() && timestampType.precision() > MAX_TIMESTAMP_PRECISION) { + throw new IllegalArgumentException( + String.format( + "OceanBase MySQL mode cannot preserve timestamp precision %d; " + + "the maximum supported precision is %d", + timestampType.precision(), MAX_TIMESTAMP_PRECISION)); + } String baseType = timestampType.hasTimeZone() ? TIMESTAMP : DATETIME; return timestampType.hasPrecisionSet() ? String.format("%s(%d)", baseType, timestampType.precision()) diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java index 2026d9140a2..0ab05140371 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java @@ -99,10 +99,29 @@ public void testFromGravitinoType() { () -> OCEANBASE_TYPE_CONVERTER.fromGravitino(Types.UnparsedType.of(USER_DEFINED_TYPE))); } + @Test + public void testRejectNanosecondTimestampTypes() { + assertFromGravitinoRejected( + Types.TimestampType.withoutTimeZone(9), + "OceanBase MySQL mode cannot preserve timestamp precision 9; " + + "the maximum supported precision is 6"); + assertFromGravitinoRejected( + Types.TimestampType.withTimeZone(9), + "OceanBase MySQL mode cannot preserve timestamp precision 9; " + + "the maximum supported precision is 6"); + } + protected void checkGravitinoTypeToJdbcType(String jdbcTypeName, Type gravitinoType) { Assertions.assertEquals(jdbcTypeName, OCEANBASE_TYPE_CONVERTER.fromGravitino(gravitinoType)); } + private void assertFromGravitinoRejected(Type type, String expectedMessage) { + IllegalArgumentException exception = + Assertions.assertThrows( + IllegalArgumentException.class, () -> OCEANBASE_TYPE_CONVERTER.fromGravitino(type)); + Assertions.assertEquals(expectedMessage, exception.getMessage()); + } + protected void checkJdbcTypeToGravitinoType( Type gravitinoType, String jdbcTypeName, diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java index 260d851ac7f..b65b5c7f701 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java @@ -69,6 +69,7 @@ import org.apache.gravitino.rel.indexes.Index; import org.apache.gravitino.rel.indexes.Indexes; import org.apache.gravitino.rel.types.Decimal; +import org.apache.gravitino.rel.types.Type; import org.apache.gravitino.rel.types.Types; import org.apache.gravitino.utils.RandomNameUtils; import org.junit.jupiter.api.AfterAll; @@ -1990,4 +1991,42 @@ void testTimeTypePrecision() { } } } + + @Test + void testRejectNanosecondTimestampsWithoutCreatingTables() { + assertCreateRejectedWithoutSideEffect( + "timestamp_ns", + Types.TimestampType.withoutTimeZone(9), + "OceanBase MySQL mode cannot preserve timestamp precision 9; " + + "the maximum supported precision is 6"); + assertCreateRejectedWithoutSideEffect( + "timestamptz_ns", + Types.TimestampType.withTimeZone(9), + "OceanBase MySQL mode cannot preserve timestamp precision 9; " + + "the maximum supported precision is 6"); + } + + private void assertCreateRejectedWithoutSideEffect( + String tablePrefix, Type type, String expectedMessage) { + TableCatalog tableCatalog = catalog.asTableCatalog(); + NameIdentifier tableIdentifier = + NameIdentifier.of(schemaName, GravitinoITUtils.genRandomName(tablePrefix)); + + IllegalArgumentException exception = + Assertions.assertThrows( + IllegalArgumentException.class, + () -> + tableCatalog.createTable( + tableIdentifier, + new Column[] {Column.of("value", type)}, + "unsupported type", + Collections.emptyMap())); + + Assertions.assertTrue( + exception.getMessage().contains(expectedMessage), + String.format( + "Expected rejection to contain \"%s\", but was: %s", + expectedMessage, exception.getMessage())); + Assertions.assertFalse(tableCatalog.tableExists(tableIdentifier)); + } } diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java index 2897660bfc2..1bba1240283 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java @@ -27,6 +27,7 @@ import org.apache.gravitino.rel.expressions.literals.Literals; import org.apache.gravitino.rel.expressions.transforms.Transforms; import org.apache.gravitino.rel.indexes.Indexes; +import org.apache.gravitino.rel.types.Type; import org.apache.gravitino.rel.types.Types; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -89,4 +90,25 @@ public void testCreateTableWithNonEmptyStringDefaultValue() { sql.contains("DEFAULT " + converter.fromGravitino(col1.defaultValue())), "Should contain DEFAULT value but was: " + sql); } + + @Test + public void testRejectNanosecondTimestampsBeforeSqlGeneration() { + assertCreateTableRejected(Types.TimestampType.withoutTimeZone(9)); + assertCreateTableRejected(Types.TimestampType.withTimeZone(9)); + } + + private void assertCreateTableRejected(Type type) { + TestableOceanBaseTableOperations ops = new TestableOceanBaseTableOperations(); + JdbcColumn column = + JdbcColumn.builder().withName("col1").withType(type).withNullable(true).build(); + + IllegalArgumentException exception = + Assertions.assertThrows( + IllegalArgumentException.class, + () -> ops.createTableSql("test_table", new JdbcColumn[] {column})); + Assertions.assertEquals( + "OceanBase MySQL mode cannot preserve timestamp precision 9; " + + "the maximum supported precision is 6", + exception.getMessage()); + } } diff --git a/docs/jdbc-oceanbase-catalog.md b/docs/jdbc-oceanbase-catalog.md index 222b0d36439..7e80582d045 100644 --- a/docs/jdbc-oceanbase-catalog.md +++ b/docs/jdbc-oceanbase-catalog.md @@ -144,6 +144,15 @@ Refer to [Manage Relational Metadata Using Gravitino](./manage-relational-metada | `FixedChar` | `FixedChar` | | `Binary` | `Binary` | +#### Iceberg V3 type interoperability + +The OceanBase catalog either preserves an Iceberg V3 type exactly or rejects it before executing +DDL. The pinned OceanBase 4.2.1 test environment uses MySQL compatibility mode. + +| Gravitino type family | OceanBase behavior | +|-----------------------------------------|------------------------------------------------------------------------------------------------------| +| `Timestamp(9)`, `Timestamp_tz(9)` | Rejected because MySQL-mode `DATETIME` and `TIMESTAMP` preserve at most microsecond precision (0–6). | + :::info OceanBase doesn't support Gravitino `Boolean` `Fixed` `Struct` `List` `Map` `IntervalDay` `IntervalYear` `Union` `UUID` type. Meanwhile, the data types other than listed above are mapped to Gravitino **[External Type](./manage-relational-metadata-using-gravitino.md#external-type)** that represents an unresolvable data type since 0.6.0-incubating. From d1ce37ef11442f6f549ee58d5b875e0a76d066d3 Mon Sep 17 00:00:00 2001 From: Nevin Zheng Date: Sat, 18 Jul 2026 06:46:49 -0700 Subject: [PATCH 2/5] fix(oceanbase): reject variant columns --- .../converter/OceanBaseTypeConverter.java | 3 +++ .../converter/TestOceanBaseTypeConverter.java | 7 +++++++ .../integration/test/CatalogOceanBaseIT.java | 8 +++++++ ...OceanBaseTableOperationsSqlGeneration.java | 21 ++++++++++++------- docs/jdbc-oceanbase-catalog.md | 1 + 5 files changed, 33 insertions(+), 7 deletions(-) diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java index e44396e0801..bca5e711bdb 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java @@ -158,6 +158,9 @@ public String fromGravitino(Type type) { return type.simpleString(); } else if (type instanceof Types.BinaryType) { return type.simpleString(); + } else if (type instanceof Types.VariantType) { + throw new IllegalArgumentException( + "OceanBase JSON cannot losslessly preserve the Gravitino variant type"); } else if (type instanceof Types.ExternalType) { return ((Types.ExternalType) type).catalogString(); } diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java index 0ab05140371..649ffb786b2 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java @@ -111,6 +111,13 @@ public void testRejectNanosecondTimestampTypes() { + "the maximum supported precision is 6"); } + @Test + public void testRejectVariantType() { + assertFromGravitinoRejected( + Types.VariantType.get(), + "OceanBase JSON cannot losslessly preserve the Gravitino variant type"); + } + protected void checkGravitinoTypeToJdbcType(String jdbcTypeName, Type gravitinoType) { Assertions.assertEquals(jdbcTypeName, OCEANBASE_TYPE_CONVERTER.fromGravitino(gravitinoType)); } diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java index b65b5c7f701..1b22201a8c5 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java @@ -2006,6 +2006,14 @@ void testRejectNanosecondTimestampsWithoutCreatingTables() { + "the maximum supported precision is 6"); } + @Test + void testRejectVariantWithoutCreatingTable() { + assertCreateRejectedWithoutSideEffect( + "variant", + Types.VariantType.get(), + "OceanBase JSON cannot losslessly preserve the Gravitino variant type"); + } + private void assertCreateRejectedWithoutSideEffect( String tablePrefix, Type type, String expectedMessage) { TableCatalog tableCatalog = catalog.asTableCatalog(); diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java index 1bba1240283..c1b5f15d4b2 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java @@ -93,11 +93,21 @@ public void testCreateTableWithNonEmptyStringDefaultValue() { @Test public void testRejectNanosecondTimestampsBeforeSqlGeneration() { - assertCreateTableRejected(Types.TimestampType.withoutTimeZone(9)); - assertCreateTableRejected(Types.TimestampType.withTimeZone(9)); + String expectedMessage = + "OceanBase MySQL mode cannot preserve timestamp precision 9; " + + "the maximum supported precision is 6"; + assertCreateTableRejected(Types.TimestampType.withoutTimeZone(9), expectedMessage); + assertCreateTableRejected(Types.TimestampType.withTimeZone(9), expectedMessage); } - private void assertCreateTableRejected(Type type) { + @Test + public void testRejectVariantBeforeSqlGeneration() { + assertCreateTableRejected( + Types.VariantType.get(), + "OceanBase JSON cannot losslessly preserve the Gravitino variant type"); + } + + private void assertCreateTableRejected(Type type, String expectedMessage) { TestableOceanBaseTableOperations ops = new TestableOceanBaseTableOperations(); JdbcColumn column = JdbcColumn.builder().withName("col1").withType(type).withNullable(true).build(); @@ -106,9 +116,6 @@ private void assertCreateTableRejected(Type type) { Assertions.assertThrows( IllegalArgumentException.class, () -> ops.createTableSql("test_table", new JdbcColumn[] {column})); - Assertions.assertEquals( - "OceanBase MySQL mode cannot preserve timestamp precision 9; " - + "the maximum supported precision is 6", - exception.getMessage()); + Assertions.assertEquals(expectedMessage, exception.getMessage()); } } diff --git a/docs/jdbc-oceanbase-catalog.md b/docs/jdbc-oceanbase-catalog.md index 7e80582d045..fa2083ab863 100644 --- a/docs/jdbc-oceanbase-catalog.md +++ b/docs/jdbc-oceanbase-catalog.md @@ -152,6 +152,7 @@ DDL. The pinned OceanBase 4.2.1 test environment uses MySQL compatibility mode. | Gravitino type family | OceanBase behavior | |-----------------------------------------|------------------------------------------------------------------------------------------------------| | `Timestamp(9)`, `Timestamp_tz(9)` | Rejected because MySQL-mode `DATETIME` and `TIMESTAMP` preserve at most microsecond precision (0–6). | +| `Variant` | Rejected because OceanBase `JSON` cannot losslessly preserve the complete Gravitino variant domain. | :::info OceanBase doesn't support Gravitino `Boolean` `Fixed` `Struct` `List` `Map` `IntervalDay` `IntervalYear` `Union` `UUID` type. From 6fe7bf1f2b314c7d227f9f49e2c6fe5832ae935b Mon Sep 17 00:00:00 2001 From: Nevin Zheng Date: Sat, 18 Jul 2026 06:47:54 -0700 Subject: [PATCH 3/5] fix(oceanbase): reject unknown columns --- .../oceanbase/converter/OceanBaseTypeConverter.java | 3 +++ .../oceanbase/converter/TestOceanBaseTypeConverter.java | 7 +++++++ .../oceanbase/integration/test/CatalogOceanBaseIT.java | 8 ++++++++ .../TestOceanBaseTableOperationsSqlGeneration.java | 7 +++++++ docs/jdbc-oceanbase-catalog.md | 1 + 5 files changed, 26 insertions(+) diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java index bca5e711bdb..a17e1a80977 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java @@ -158,6 +158,9 @@ public String fromGravitino(Type type) { return type.simpleString(); } else if (type instanceof Types.BinaryType) { return type.simpleString(); + } else if (type instanceof Types.NullType) { + throw new IllegalArgumentException( + "OceanBase has no column type that preserves the Gravitino unknown type"); } else if (type instanceof Types.VariantType) { throw new IllegalArgumentException( "OceanBase JSON cannot losslessly preserve the Gravitino variant type"); diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java index 649ffb786b2..289b3bc90dd 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java @@ -118,6 +118,13 @@ public void testRejectVariantType() { "OceanBase JSON cannot losslessly preserve the Gravitino variant type"); } + @Test + public void testRejectUnknownType() { + assertFromGravitinoRejected( + Types.NullType.get(), + "OceanBase has no column type that preserves the Gravitino unknown type"); + } + protected void checkGravitinoTypeToJdbcType(String jdbcTypeName, Type gravitinoType) { Assertions.assertEquals(jdbcTypeName, OCEANBASE_TYPE_CONVERTER.fromGravitino(gravitinoType)); } diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java index 1b22201a8c5..0221fde2165 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java @@ -2014,6 +2014,14 @@ void testRejectVariantWithoutCreatingTable() { "OceanBase JSON cannot losslessly preserve the Gravitino variant type"); } + @Test + void testRejectUnknownWithoutCreatingTable() { + assertCreateRejectedWithoutSideEffect( + "unknown", + Types.NullType.get(), + "OceanBase has no column type that preserves the Gravitino unknown type"); + } + private void assertCreateRejectedWithoutSideEffect( String tablePrefix, Type type, String expectedMessage) { TableCatalog tableCatalog = catalog.asTableCatalog(); diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java index c1b5f15d4b2..39c05c67cd7 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java @@ -107,6 +107,13 @@ public void testRejectVariantBeforeSqlGeneration() { "OceanBase JSON cannot losslessly preserve the Gravitino variant type"); } + @Test + public void testRejectUnknownBeforeSqlGeneration() { + assertCreateTableRejected( + Types.NullType.get(), + "OceanBase has no column type that preserves the Gravitino unknown type"); + } + private void assertCreateTableRejected(Type type, String expectedMessage) { TestableOceanBaseTableOperations ops = new TestableOceanBaseTableOperations(); JdbcColumn column = diff --git a/docs/jdbc-oceanbase-catalog.md b/docs/jdbc-oceanbase-catalog.md index fa2083ab863..1ffcf4ab957 100644 --- a/docs/jdbc-oceanbase-catalog.md +++ b/docs/jdbc-oceanbase-catalog.md @@ -153,6 +153,7 @@ DDL. The pinned OceanBase 4.2.1 test environment uses MySQL compatibility mode. |-----------------------------------------|------------------------------------------------------------------------------------------------------| | `Timestamp(9)`, `Timestamp_tz(9)` | Rejected because MySQL-mode `DATETIME` and `TIMESTAMP` preserve at most microsecond precision (0–6). | | `Variant` | Rejected because OceanBase `JSON` cannot losslessly preserve the complete Gravitino variant domain. | +| `Unknown` (`NullType`) | Rejected because OceanBase has no corresponding column type. | :::info OceanBase doesn't support Gravitino `Boolean` `Fixed` `Struct` `List` `Map` `IntervalDay` `IntervalYear` `Union` `UUID` type. From 08cca7a9b4d98c1179b859f6cce6861b6738af96 Mon Sep 17 00:00:00 2001 From: Nevin Zheng Date: Sat, 18 Jul 2026 06:49:19 -0700 Subject: [PATCH 4/5] fix(oceanbase): reject geometry columns --- .../oceanbase/converter/OceanBaseTypeConverter.java | 3 +++ .../converter/TestOceanBaseTypeConverter.java | 8 ++++++++ .../oceanbase/integration/test/CatalogOceanBaseIT.java | 10 ++++++++++ .../TestOceanBaseTableOperationsSqlGeneration.java | 7 +++++++ docs/jdbc-oceanbase-catalog.md | 1 + 5 files changed, 29 insertions(+) diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java index a17e1a80977..77c3ba1164b 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java @@ -164,6 +164,9 @@ public String fromGravitino(Type type) { } else if (type instanceof Types.VariantType) { throw new IllegalArgumentException( "OceanBase JSON cannot losslessly preserve the Gravitino variant type"); + } else if (type instanceof Types.GeometryType) { + throw new IllegalArgumentException( + "OceanBase JDBC metadata cannot preserve Gravitino geometry CRS/SRID metadata"); } else if (type instanceof Types.ExternalType) { return ((Types.ExternalType) type).catalogString(); } diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java index 289b3bc90dd..3f6d55a0492 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java @@ -125,6 +125,14 @@ public void testRejectUnknownType() { "OceanBase has no column type that preserves the Gravitino unknown type"); } + @Test + public void testRejectGeometryTypes() { + String expectedMessage = + "OceanBase JDBC metadata cannot preserve Gravitino geometry CRS/SRID metadata"; + assertFromGravitinoRejected(Types.GeometryType.crs84(), expectedMessage); + assertFromGravitinoRejected(Types.GeometryType.of("EPSG:3857"), expectedMessage); + } + protected void checkGravitinoTypeToJdbcType(String jdbcTypeName, Type gravitinoType) { Assertions.assertEquals(jdbcTypeName, OCEANBASE_TYPE_CONVERTER.fromGravitino(gravitinoType)); } diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java index 0221fde2165..a89a1c20896 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java @@ -2022,6 +2022,16 @@ void testRejectUnknownWithoutCreatingTable() { "OceanBase has no column type that preserves the Gravitino unknown type"); } + @Test + void testRejectGeometryWithoutCreatingTables() { + String expectedMessage = + "OceanBase JDBC metadata cannot preserve Gravitino geometry CRS/SRID metadata"; + assertCreateRejectedWithoutSideEffect( + "geometry_crs84", Types.GeometryType.crs84(), expectedMessage); + assertCreateRejectedWithoutSideEffect( + "geometry_epsg3857", Types.GeometryType.of("EPSG:3857"), expectedMessage); + } + private void assertCreateRejectedWithoutSideEffect( String tablePrefix, Type type, String expectedMessage) { TableCatalog tableCatalog = catalog.asTableCatalog(); diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java index 39c05c67cd7..20926da40da 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java @@ -114,6 +114,13 @@ public void testRejectUnknownBeforeSqlGeneration() { "OceanBase has no column type that preserves the Gravitino unknown type"); } + @Test + public void testRejectGeometryBeforeSqlGeneration() { + assertCreateTableRejected( + Types.GeometryType.crs84(), + "OceanBase JDBC metadata cannot preserve Gravitino geometry CRS/SRID metadata"); + } + private void assertCreateTableRejected(Type type, String expectedMessage) { TestableOceanBaseTableOperations ops = new TestableOceanBaseTableOperations(); JdbcColumn column = diff --git a/docs/jdbc-oceanbase-catalog.md b/docs/jdbc-oceanbase-catalog.md index 1ffcf4ab957..532106b2a58 100644 --- a/docs/jdbc-oceanbase-catalog.md +++ b/docs/jdbc-oceanbase-catalog.md @@ -154,6 +154,7 @@ DDL. The pinned OceanBase 4.2.1 test environment uses MySQL compatibility mode. | `Timestamp(9)`, `Timestamp_tz(9)` | Rejected because MySQL-mode `DATETIME` and `TIMESTAMP` preserve at most microsecond precision (0–6). | | `Variant` | Rejected because OceanBase `JSON` cannot losslessly preserve the complete Gravitino variant domain. | | `Unknown` (`NullType`) | Rejected because OceanBase has no corresponding column type. | +| `Geometry(crs)` | Rejected because the connector cannot currently reload OceanBase spatial subtype and SRID metadata. | :::info OceanBase doesn't support Gravitino `Boolean` `Fixed` `Struct` `List` `Map` `IntervalDay` `IntervalYear` `Union` `UUID` type. From 2ad327edf28e3beea270aae9a5be8c39c5eadaeb Mon Sep 17 00:00:00 2001 From: Nevin Zheng Date: Sat, 18 Jul 2026 06:50:52 -0700 Subject: [PATCH 5/5] fix(oceanbase): reject geography columns --- .../oceanbase/converter/OceanBaseTypeConverter.java | 3 +++ .../converter/TestOceanBaseTypeConverter.java | 8 ++++++++ .../oceanbase/integration/test/CatalogOceanBaseIT.java | 10 ++++++++++ .../TestOceanBaseTableOperationsSqlGeneration.java | 7 +++++++ docs/jdbc-oceanbase-catalog.md | 1 + 5 files changed, 29 insertions(+) diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java index 77c3ba1164b..9ac67ca61ac 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/converter/OceanBaseTypeConverter.java @@ -167,6 +167,9 @@ public String fromGravitino(Type type) { } else if (type instanceof Types.GeometryType) { throw new IllegalArgumentException( "OceanBase JDBC metadata cannot preserve Gravitino geometry CRS/SRID metadata"); + } else if (type instanceof Types.GeographyType) { + throw new IllegalArgumentException( + "OceanBase has no geography column type that preserves CRS and edge algorithm metadata"); } else if (type instanceof Types.ExternalType) { return ((Types.ExternalType) type).catalogString(); } diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java index 3f6d55a0492..42e3c0a4784 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/converter/TestOceanBaseTypeConverter.java @@ -133,6 +133,14 @@ public void testRejectGeometryTypes() { assertFromGravitinoRejected(Types.GeometryType.of("EPSG:3857"), expectedMessage); } + @Test + public void testRejectGeographyTypes() { + String expectedMessage = + "OceanBase has no geography column type that preserves CRS and edge algorithm metadata"; + assertFromGravitinoRejected(Types.GeographyType.crs84(), expectedMessage); + assertFromGravitinoRejected(Types.GeographyType.of("EPSG:4326", "karney"), expectedMessage); + } + protected void checkGravitinoTypeToJdbcType(String jdbcTypeName, Type gravitinoType) { Assertions.assertEquals(jdbcTypeName, OCEANBASE_TYPE_CONVERTER.fromGravitino(gravitinoType)); } diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java index a89a1c20896..c7e87bca59a 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/integration/test/CatalogOceanBaseIT.java @@ -2032,6 +2032,16 @@ void testRejectGeometryWithoutCreatingTables() { "geometry_epsg3857", Types.GeometryType.of("EPSG:3857"), expectedMessage); } + @Test + void testRejectGeographyWithoutCreatingTables() { + String expectedMessage = + "OceanBase has no geography column type that preserves CRS and edge algorithm metadata"; + assertCreateRejectedWithoutSideEffect( + "geography_crs84", Types.GeographyType.crs84(), expectedMessage); + assertCreateRejectedWithoutSideEffect( + "geography_karney", Types.GeographyType.of("EPSG:4326", "karney"), expectedMessage); + } + private void assertCreateRejectedWithoutSideEffect( String tablePrefix, Type type, String expectedMessage) { TableCatalog tableCatalog = catalog.asTableCatalog(); diff --git a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java index 20926da40da..e4dec7e43f7 100644 --- a/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java +++ b/catalogs-contrib/catalog-jdbc-oceanbase/src/test/java/org/apache/gravitino/catalog/oceanbase/operation/TestOceanBaseTableOperationsSqlGeneration.java @@ -121,6 +121,13 @@ public void testRejectGeometryBeforeSqlGeneration() { "OceanBase JDBC metadata cannot preserve Gravitino geometry CRS/SRID metadata"); } + @Test + public void testRejectGeographyBeforeSqlGeneration() { + assertCreateTableRejected( + Types.GeographyType.crs84(), + "OceanBase has no geography column type that preserves CRS and edge algorithm metadata"); + } + private void assertCreateTableRejected(Type type, String expectedMessage) { TestableOceanBaseTableOperations ops = new TestableOceanBaseTableOperations(); JdbcColumn column = diff --git a/docs/jdbc-oceanbase-catalog.md b/docs/jdbc-oceanbase-catalog.md index 532106b2a58..86a6acd8942 100644 --- a/docs/jdbc-oceanbase-catalog.md +++ b/docs/jdbc-oceanbase-catalog.md @@ -155,6 +155,7 @@ DDL. The pinned OceanBase 4.2.1 test environment uses MySQL compatibility mode. | `Variant` | Rejected because OceanBase `JSON` cannot losslessly preserve the complete Gravitino variant domain. | | `Unknown` (`NullType`) | Rejected because OceanBase has no corresponding column type. | | `Geometry(crs)` | Rejected because the connector cannot currently reload OceanBase spatial subtype and SRID metadata. | +| `Geography(crs, algorithm)` | Rejected because OceanBase has no type preserving geography edge semantics. | :::info OceanBase doesn't support Gravitino `Boolean` `Fixed` `Struct` `List` `Map` `IntervalDay` `IntervalYear` `Union` `UUID` type.