Skip to content
1 change: 1 addition & 0 deletions NEXT_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Added

### Updated
- `EnableGeoSpatialSupport` no longer requires `EnableComplexDatatypeSupport=1`. Geospatial types (GEOMETRY, GEOGRAPHY) can now be enabled independently of complex type support (ARRAY, MAP, STRUCT).

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -995,9 +995,7 @@ public boolean isComplexDatatypeSupportEnabled() {

@Override
public boolean isGeoSpatialSupportEnabled() {
// Geospatial support requires complex datatype support to be enabled
return isComplexDatatypeSupportEnabled()
&& getParameter(DatabricksJdbcUrlParams.ENABLE_GEOSPATIAL_SUPPORT).equals("1");
return getParameter(DatabricksJdbcUrlParams.ENABLE_GEOSPATIAL_SUPPORT).equals("1");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,10 @@ public Object getObject(int columnIndex) throws SQLException {
}
int columnType = resultSetMetaData.getColumnType(columnIndex);
String columnTypeName = resultSetMetaData.getColumnTypeName(columnIndex);
// Geospatial types: handle independently of complex datatype flag
if (isGeospatialType(columnTypeName)) {
return handleGeospatialType(obj, columnTypeName);
}
// separate handling for complex data types
if (isComplexType(columnTypeName)) {
return handleComplexDataTypes(obj, columnTypeName);
Expand All @@ -541,6 +545,25 @@ public Object getObject(int columnIndex) throws SQLException {
return ConverterHelper.convertSqlTypeToJavaType(columnType, obj);
}

private Object handleGeospatialType(Object obj, String columnName) throws DatabricksSQLException {
if (resultSetType == ResultSetType.SEA_INLINE) {
obj = convertGeospatialForSEAInline(obj, columnName);
}
return obj;
}

private Object convertGeospatialForSEAInline(Object obj, String columnName)
throws DatabricksSQLException {
if (columnName.startsWith(GEOMETRY)) {
return ConverterHelper.getConverterForColumnType(Types.OTHER, GEOMETRY)
.toDatabricksGeometry(obj);
} else if (columnName.startsWith(GEOGRAPHY)) {
return ConverterHelper.getConverterForColumnType(Types.OTHER, GEOGRAPHY)
.toDatabricksGeography(obj);
}
return obj;
}

private Object handleComplexDataTypes(Object obj, String columnName)
throws DatabricksSQLException {
if (resultSetType == ResultSetType.SEA_INLINE) {
Expand All @@ -558,12 +581,6 @@ private Object convertToComplexDataTypesForSEAInline(Object obj, String columnNa
return parser.parseJsonStringToDbMap(obj.toString(), columnName);
} else if (columnName.startsWith(STRUCT)) {
return parser.parseJsonStringToDbStruct(obj.toString(), columnName);
} else if (columnName.startsWith(GEOMETRY)) {
return ConverterHelper.getConverterForColumnType(Types.OTHER, GEOMETRY)
.toDatabricksGeometry(obj);
} else if (columnName.startsWith(GEOGRAPHY)) {
return ConverterHelper.getConverterForColumnType(Types.OTHER, GEOGRAPHY)
.toDatabricksGeography(obj);
}
throw new DatabricksParsingException(
"Unexpected metadata format. Type is not a COMPLEX: " + columnName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,7 @@ public Object getObject(int columnIndex) throws DatabricksSQLException {
public static boolean isComplexType(ColumnInfoTypeName type) {
return type == ColumnInfoTypeName.ARRAY
|| type == ColumnInfoTypeName.MAP
|| type == ColumnInfoTypeName.STRUCT
|| type == ColumnInfoTypeName.GEOMETRY
|| type == ColumnInfoTypeName.GEOGRAPHY;
|| type == ColumnInfoTypeName.STRUCT;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,10 +308,7 @@ public interface IDatabricksConnectionContext {
/** Returns true if driver return complex data type java objects natively as opposed to string */
boolean isComplexDatatypeSupportEnabled();

/**
* Returns true if driver returns GEOMETRY and GEOGRAPHY types natively. Requires
* isComplexDatatypeSupportEnabled() to be true
*/
/** Returns true if driver returns GEOMETRY and GEOGRAPHY types natively. */
boolean isGeoSpatialSupportEnabled();

/** Returns the size for HTTP connection pool */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public enum DatabricksJdbcUrlParams {
"0"),
ENABLE_GEOSPATIAL_SUPPORT(
"EnableGeoSpatialSupport",
"flag to enable native support of GEOMETRY and GEOGRAPHY data types. Requires EnableComplexDatatypeSupport=1",
"flag to enable native support of GEOMETRY and GEOGRAPHY data types",
"0"),
ROWS_FETCHED_PER_BLOCK(
"RowsFetchedPerBlock",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,8 @@ public static ColumnInfo getColumnInfoFromTColumnDesc(

String typeText = getTypeTextFromTypeDesc(columnDesc.getTypeDesc());

if (arrowMetadata != null && isComplexType(arrowMetadata)) {
if (arrowMetadata != null
&& (isComplexType(arrowMetadata) || isGeospatialType(arrowMetadata))) {
typeText = arrowMetadata;
if (arrowMetadata.startsWith(GEOMETRY)) {
columnInfoTypeName = ColumnInfoTypeName.GEOMETRY;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -561,18 +561,22 @@ public static String getDecimalTypeString(BigDecimal bd) {
}

/**
* Checks if the given type name represents a complex type (ARRAY, MAP, STRUCT, GEOMETRY, or
* GEOGRAPHY).
* Checks if the given type name represents a complex type (ARRAY, MAP, STRUCT).
*
* @param typeName The type name to check
* @return true if the type name starts with ARRAY, MAP, STRUCT, GEOMETRY, or GEOGRAPHY, false
* otherwise
* @return true if the type name starts with ARRAY, MAP, or STRUCT
*/
public static boolean isComplexType(String typeName) {
return typeName.startsWith(ARRAY)
|| typeName.startsWith(MAP)
|| typeName.startsWith(STRUCT)
|| typeName.startsWith(GEOMETRY)
|| typeName.startsWith(GEOGRAPHY);
return typeName.startsWith(ARRAY) || typeName.startsWith(MAP) || typeName.startsWith(STRUCT);
}

/**
* Checks if the given type name represents a geospatial type (GEOMETRY, GEOGRAPHY).
*
* @param typeName The type name to check
* @return true if the type name starts with GEOMETRY or GEOGRAPHY
*/
public static boolean isGeospatialType(String typeName) {
return typeName.startsWith(GEOMETRY) || typeName.startsWith(GEOGRAPHY);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1488,6 +1488,59 @@ public void testUseQueryForMetadataExplicitFalseOnWarehouse() throws DatabricksS
assertFalse(ctx.useQueryForMetadata());
}

// ---------------------------------------------------------------------------
// Geospatial flag independence from complex datatype flag
// ---------------------------------------------------------------------------

@Test
public void testGeospatialEnabled_complexDisabled() throws DatabricksSQLException {
IDatabricksConnectionContext ctx =
DatabricksConnectionContext.parse(
TestConstants.VALID_URL_1 + ";EnableGeoSpatialSupport=1;EnableComplexDatatypeSupport=0",
properties);
assertTrue(ctx.isGeoSpatialSupportEnabled());
assertFalse(ctx.isComplexDatatypeSupportEnabled());
}

@Test
public void testGeospatialDisabled_complexEnabled() throws DatabricksSQLException {
IDatabricksConnectionContext ctx =
DatabricksConnectionContext.parse(
TestConstants.VALID_URL_1 + ";EnableGeoSpatialSupport=0;EnableComplexDatatypeSupport=1",
properties);
assertFalse(ctx.isGeoSpatialSupportEnabled());
assertTrue(ctx.isComplexDatatypeSupportEnabled());
}

@Test
public void testGeospatialAndComplexBothEnabled() throws DatabricksSQLException {
IDatabricksConnectionContext ctx =
DatabricksConnectionContext.parse(
TestConstants.VALID_URL_1 + ";EnableGeoSpatialSupport=1;EnableComplexDatatypeSupport=1",
properties);
assertTrue(ctx.isGeoSpatialSupportEnabled());
assertTrue(ctx.isComplexDatatypeSupportEnabled());
}

@Test
public void testGeospatialAndComplexBothDisabled() throws DatabricksSQLException {
IDatabricksConnectionContext ctx =
DatabricksConnectionContext.parse(
TestConstants.VALID_URL_1 + ";EnableGeoSpatialSupport=0;EnableComplexDatatypeSupport=0",
properties);
assertFalse(ctx.isGeoSpatialSupportEnabled());
assertFalse(ctx.isComplexDatatypeSupportEnabled());
}

@Test
public void testGeospatialDefaultDisabled() throws DatabricksSQLException {
// Neither flag set — both default to disabled
IDatabricksConnectionContext ctx =
DatabricksConnectionContext.parse(TestConstants.VALID_URL_1, properties);
assertFalse(ctx.isGeoSpatialSupportEnabled());
assertFalse(ctx.isComplexDatatypeSupportEnabled());
}

// ---------------------------------------------------------------------------
// Client type selection with Thrift-native metadata params
// ---------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -765,9 +765,9 @@ public void testJsonArrayFormatMetadataWithGeospatialFlagDisabled() throws SQLEx

@Test
public void testJsonArrayWithComplexTypesEnabledButGeospatialDisabled() throws SQLException {
// This test validates the important scenario where EnableComplexDatatypeSupport=1
// but EnableGeoSpatialSupport=0 (disabled). This simulates real-world usage where
// users want complex types (ARRAY, MAP, STRUCT) but want geospatial data as strings.
// This test validates that with EnableGeoSpatialSupport=0, geospatial columns
// report as STRING in metadata regardless of the EnableComplexDatatypeSupport setting.
// The two flags are independent.
//
// Expected behavior:
// - GEOMETRY/GEOGRAPHY column types should report as STRING in metadata
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,9 +367,9 @@ void testJsonArrayFormatWithGeospatialTypesWhenFlagEnabled() throws DatabricksSQ

@Test
void testJsonArrayWithComplexTypesEnabledButGeospatialDisabled() throws DatabricksSQLException {
// This test validates the scenario where EnableComplexDatatypeSupport=1 but
// EnableGeoSpatialSupport=0 (disabled). This simulates a real-world scenario where
// users enable complex types for ARRAY/MAP/STRUCT but want geospatial data as strings.
// This test validates that with EnableGeoSpatialSupport=0, geospatial columns
// return as strings regardless of EnableComplexDatatypeSupport setting.
// The two flags are independent.
//
// Expected behavior:
// - Geospatial column TYPES in metadata should report as STRING
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,15 @@ public void testComplexTypeHandling() {
assertTrue(ArrowStreamResult.isComplexType(ColumnInfoTypeName.ARRAY));
assertTrue(ArrowStreamResult.isComplexType(ColumnInfoTypeName.MAP));
assertTrue(ArrowStreamResult.isComplexType(ColumnInfoTypeName.STRUCT));
assertTrue(ArrowStreamResult.isComplexType(ColumnInfoTypeName.GEOMETRY));
assertTrue(ArrowStreamResult.isComplexType(ColumnInfoTypeName.GEOGRAPHY));

// Geospatial types are NOT complex types — they have independent handling
assertFalse(ArrowStreamResult.isComplexType(ColumnInfoTypeName.GEOMETRY));
assertFalse(ArrowStreamResult.isComplexType(ColumnInfoTypeName.GEOGRAPHY));

// Geospatial type check is separate
assertTrue(ArrowStreamResult.isGeospatialType(ColumnInfoTypeName.GEOMETRY));
assertTrue(ArrowStreamResult.isGeospatialType(ColumnInfoTypeName.GEOGRAPHY));
assertFalse(ArrowStreamResult.isGeospatialType(ColumnInfoTypeName.ARRAY));

// Non-complex types should return false
assertFalse(ArrowStreamResult.isComplexType(ColumnInfoTypeName.INT));
Expand Down Expand Up @@ -338,8 +345,8 @@ private Schema createTestSchema() {

@Test
public void testGeospatialTypeWithGeoSpatialSupportDisabled() throws Exception {
// Setup connection context with geospatial support disabled
// (EnableComplexDatatypeSupport=1, but EnableGeoSpatialSupport=0)
// Setup connection context with geospatial support disabled (EnableGeoSpatialSupport=0)
// Complex datatype flag is independent and has no effect on geospatial behavior
Properties props = new Properties();
props.setProperty("EnableComplexDatatypeSupport", "1");
props.setProperty("EnableGeoSpatialSupport", "0");
Expand Down Expand Up @@ -396,32 +403,35 @@ public void testGeospatialTypeWithGeoSpatialSupportDisabled() throws Exception {
}

@Test
public void testGeospatialTypeWithBothFlagsEnabled() throws Exception {
// Setup connection context with both complex datatype and geospatial support enabled
public void testGeospatialEnabledIndependentlyOfComplexDatatype() throws Exception {
// Geospatial can be enabled with or without complex datatype support
Properties props = new Properties();
props.setProperty("EnableComplexDatatypeSupport", "1");
props.setProperty("EnableGeoSpatialSupport", "1");
IDatabricksConnectionContext connectionContext =
DatabricksConnectionContextFactory.create(JDBC_URL, props);

// Verify both flags are enabled
assertTrue(connectionContext.isComplexDatatypeSupportEnabled());
assertTrue(connectionContext.isGeoSpatialSupportEnabled());
IDatabricksConnectionContext ctx1 = DatabricksConnectionContextFactory.create(JDBC_URL, props);
assertTrue(ctx1.isComplexDatatypeSupportEnabled());
assertTrue(ctx1.isGeoSpatialSupportEnabled());

// Geospatial enabled without complex datatypes
Properties props2 = new Properties();
props2.setProperty("EnableComplexDatatypeSupport", "0");
props2.setProperty("EnableGeoSpatialSupport", "1");
IDatabricksConnectionContext ctx2 = DatabricksConnectionContextFactory.create(JDBC_URL, props2);
assertFalse(ctx2.isComplexDatatypeSupportEnabled());
assertTrue(ctx2.isGeoSpatialSupportEnabled());
}

@Test
public void testGeospatialSupportRequiresComplexDatatypeSupport() throws Exception {
// Test that EnableGeoSpatialSupport=1 alone (without EnableComplexDatatypeSupport) doesn't
// enable geospatial
public void testGeospatialSupportIndependentOfComplexDatatypeSupport() throws Exception {
// Geospatial support is independent of complex datatype support — can be enabled alone
Properties props = new Properties();
props.setProperty("EnableComplexDatatypeSupport", "0");
props.setProperty("EnableGeoSpatialSupport", "1");
IDatabricksConnectionContext connectionContext =
DatabricksConnectionContextFactory.create(JDBC_URL, props);

// Verify that geospatial support is disabled because complex datatype support is disabled
assertFalse(connectionContext.isComplexDatatypeSupportEnabled());
assertFalse(connectionContext.isGeoSpatialSupportEnabled());
assertTrue(connectionContext.isGeoSpatialSupportEnabled());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
* (Thrift/SEA), Serialization (Arrow/Inline), CloudFetch, GeoSpatial support, and Complex type
* support.
*
* <p>Geospatial objects (IGeometry/IGeography) are returned only when both EnableGeoSpatialSupport
* and EnableComplexDatatypeSupport are enabled AND not in Thrift+Inline mode. Otherwise, returns as
* STRING.
* <p>Geospatial objects (IGeometry/IGeography) are returned when EnableGeoSpatialSupport is enabled
* AND not in Thrift+Inline mode. EnableComplexDatatypeSupport is independent and not required.
* Otherwise, returns as STRING.
*/
public class GeospatialTests {

Expand Down Expand Up @@ -138,10 +138,10 @@ void testGeospatialPoint(
assertTrue(rs.next(), "Should have at least one row for config: " + desc);

// Geospatial objects returned only when:
// 1. Both EnableGeoSpatialSupport=1 AND EnableComplexDatatypeSupport=1
// 1. EnableGeoSpatialSupport=1 (independent of EnableComplexDatatypeSupport)
// 2. NOT in Thrift + Inline mode (Thrift doesn't support geospatial without Arrow)
boolean shouldReturnGeospatialObjects =
enableGeoSupport == 1 && enableComplexSupport == 1 && !(useThrift == 1 && enableArrow == 0);
enableGeoSupport == 1 && !(useThrift == 1 && enableArrow == 0);

if (shouldReturnGeospatialObjects) {
validateGeospatialEnabled(rs, rsm);
Expand Down Expand Up @@ -193,10 +193,10 @@ void testGeometryAny(
}

// Geospatial objects returned only when:
// 1. Both EnableGeoSpatialSupport=1 AND EnableComplexDatatypeSupport=1
// 1. EnableGeoSpatialSupport=1 (independent of EnableComplexDatatypeSupport)
// 2. NOT in Thrift + Inline mode
boolean shouldReturnGeospatialObjects =
enableGeoSupport == 1 && enableComplexSupport == 1 && !(useThrift == 1 && enableArrow == 0);
enableGeoSupport == 1 && !(useThrift == 1 && enableArrow == 0);

if (shouldReturnGeospatialObjects) {
validateGeometryAnyEnabled(rs, rsm);
Expand Down
Loading