From ab7556e416c52e3b55fd4976ecafd86590bdc1ed Mon Sep 17 00:00:00 2001 From: Sreekanth Vadigi Date: Wed, 10 Sep 2025 01:12:40 +0530 Subject: [PATCH 01/14] Geospatial datatype support Signed-off-by: Sreekanth Vadigi --- pom.xml | 5 + .../impl/AbstractDatabricksGeospatial.java | 109 ++++++++++++++ .../jdbc/api/impl/DatabricksGeography.java | 17 +++ .../jdbc/api/impl/DatabricksGeometry.java | 17 +++ .../jdbc/api/impl/DatabricksGeospatial.java | 53 +++++++ .../jdbc/api/impl/DatabricksResultSet.java | 26 +++- .../api/impl/DatabricksResultSetMetaData.java | 30 +++- .../api/impl/arrow/ArrowStreamResult.java | 6 +- .../ArrowToJavaObjectConverter.java | 65 ++++++++- .../api/impl/converters/ConverterHelper.java | 33 +++++ .../impl/converters/GeospatialConverter.java | 82 +++++++++++ .../api/impl/converters/ObjectConverter.java | 20 +++ .../api/impl/converters/WKTConverter.java | 133 ++++++++++++++++++ .../jdbc/common/util/DatabricksTypeUtil.java | 10 ++ .../ArrowToJavaObjectConverterTest.java | 80 ++++++++++- .../api/impl/converters/WKTConverterTest.java | 119 ++++++++++++++++ 16 files changed, 786 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/databricks/jdbc/api/impl/AbstractDatabricksGeospatial.java create mode 100644 src/main/java/com/databricks/jdbc/api/impl/DatabricksGeography.java create mode 100644 src/main/java/com/databricks/jdbc/api/impl/DatabricksGeometry.java create mode 100644 src/main/java/com/databricks/jdbc/api/impl/DatabricksGeospatial.java create mode 100644 src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java create mode 100644 src/main/java/com/databricks/jdbc/api/impl/converters/WKTConverter.java create mode 100644 src/test/java/com/databricks/jdbc/api/impl/converters/WKTConverterTest.java diff --git a/pom.xml b/pom.xml index b82faa9fd8..a5f04968bb 100644 --- a/pom.xml +++ b/pom.xml @@ -287,6 +287,11 @@ resilience4j-core ${resilience4j.version} + + org.locationtech.jts + jts-core + 1.20.0 + diff --git a/src/main/java/com/databricks/jdbc/api/impl/AbstractDatabricksGeospatial.java b/src/main/java/com/databricks/jdbc/api/impl/AbstractDatabricksGeospatial.java new file mode 100644 index 0000000000..6fec75a9ea --- /dev/null +++ b/src/main/java/com/databricks/jdbc/api/impl/AbstractDatabricksGeospatial.java @@ -0,0 +1,109 @@ +package com.databricks.jdbc.api.impl; + +import com.databricks.jdbc.api.impl.converters.WKTConverter; +import com.databricks.jdbc.exception.DatabricksValidationException; +import com.databricks.jdbc.log.JdbcLogger; +import com.databricks.jdbc.log.JdbcLoggerFactory; +import java.util.Objects; + +/** + * Abstract base class for geospatial data types in Databricks JDBC driver. + * + *

This class provides common functionality for both GEOMETRY and GEOGRAPHY types, including + * storage of WKT (Well-Known Text) format data and access to both WKT and WKB representations. + */ +public abstract class AbstractDatabricksGeospatial implements DatabricksGeospatial { + + private static final JdbcLogger LOGGER = + JdbcLoggerFactory.getLogger(AbstractDatabricksGeospatial.class); + + private final String wkt; + private final int srid; + + /** + * Constructs an AbstractDatabricksGeospatial with the specified WKT and SRID. + * + * @param wkt the Well-Known Text representation of the geospatial object + * @param srid the Spatial Reference System Identifier + * @throws DatabricksValidationException if the WKT is invalid + */ + protected AbstractDatabricksGeospatial(String wkt, int srid) + throws DatabricksValidationException { + if (wkt == null || wkt.trim().isEmpty()) { + throw new DatabricksValidationException("WKT string cannot be null or empty"); + } + + this.wkt = wkt.trim(); + this.srid = srid; + } + + /** + * Returns the Well-Known Binary (WKB) representation of the geospatial object. + * + * @return the WKB representation as a byte array + * @throws DatabricksValidationException if WKT to WKB conversion fails + */ + @Override + public byte[] getWkb() throws DatabricksValidationException { + return WKTConverter.toWKB(wkt); + } + + /** + * Returns the Spatial Reference System Identifier (SRID) of the geospatial object. + * + * @return the SRID value + */ + @Override + public int getSrid() { + return srid; + } + + /** + * Returns the Well-Known Text (WKT) representation of the geospatial object. + * + * @return the WKT string + */ + @Override + public String getWkt() { + return wkt; + } + + /** + * Returns a string representation of the geospatial object in EWKT format. + * + * @return the EWKT string representation + */ + @Override + public String toString() { + return String.format("SRID=%d;%s", srid, wkt); + } + + /** + * Checks if this geospatial object is equal to another object. + * + * @param obj the object to compare + * @return true if the objects are equal, false otherwise + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + AbstractDatabricksGeospatial that = (AbstractDatabricksGeospatial) obj; + return srid == that.srid && wkt.equals(that.wkt); + } + + /** + * Returns the hash code for this geospatial object. + * + * @return the hash code + */ + @Override + public int hashCode() { + return Objects.hash(wkt, srid); + } +} diff --git a/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeography.java b/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeography.java new file mode 100644 index 0000000000..a1532af840 --- /dev/null +++ b/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeography.java @@ -0,0 +1,17 @@ +package com.databricks.jdbc.api.impl; + +import com.databricks.jdbc.exception.DatabricksValidationException; + +public class DatabricksGeography extends AbstractDatabricksGeospatial { + + /** + * Constructs a DatabricksGeography with the specified WKT and SRID. + * + * @param wkt the Well-Known Text representation of the geography + * @param srid the Spatial Reference System Identifier + * @throws DatabricksValidationException if the WKT is invalid + */ + public DatabricksGeography(String wkt, int srid) throws DatabricksValidationException { + super(wkt, srid); + } +} diff --git a/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeometry.java b/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeometry.java new file mode 100644 index 0000000000..a5a019aba4 --- /dev/null +++ b/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeometry.java @@ -0,0 +1,17 @@ +package com.databricks.jdbc.api.impl; + +import com.databricks.jdbc.exception.DatabricksValidationException; + +public class DatabricksGeometry extends AbstractDatabricksGeospatial { + + /** + * Constructs a DatabricksGeometry with the specified WKT and SRID. + * + * @param wkt the Well-Known Text representation of the geometry + * @param srid the Spatial Reference System Identifier + * @throws DatabricksValidationException if the WKT is invalid + */ + public DatabricksGeometry(String wkt, int srid) throws DatabricksValidationException { + super(wkt, srid); + } +} diff --git a/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeospatial.java b/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeospatial.java new file mode 100644 index 0000000000..ad6cdace23 --- /dev/null +++ b/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeospatial.java @@ -0,0 +1,53 @@ +package com.databricks.jdbc.api.impl; + +import com.databricks.jdbc.exception.DatabricksValidationException; + +/** + * Interface for geospatial data types in Databricks JDBC driver. + * + *

This interface provides common functionality for both GEOMETRY and GEOGRAPHY types, allowing + * access to Well-Known Text (WKT), Well-Known Binary (WKB) representation and Spatial Reference + * System Identifier (SRID). + * + *

Following the established patterns of DatabricksStruct, DatabricksArray, and DatabricksMap, + * this interface enables consistent handling of geospatial data across the JDBC driver. + */ +public interface DatabricksGeospatial { + + /** + * Returns the Well-Known Binary (WKB) representation of the geospatial object. + * + *

WKB is a binary format for representing geometry data that is compact and suitable for + * storage and transmission. This method converts the internal representation to WKB format on + * demand. + * + * @return the WKB representation as a byte array + * @throws DatabricksValidationException if WKT to WKB conversion fails + */ + byte[] getWkb() throws DatabricksValidationException; + + /** + * Returns the Spatial Reference System Identifier (SRID) of the geospatial object. + * + *

SRID identifies the coordinate system used by the geometry. Common values include: + * + *

    + *
  • 4326 - WGS 84 (World Geodetic System 1984) + *
  • 3857 - Web Mercator + *
  • 0 - No SRID specified + *
+ * + * @return the SRID value + */ + int getSrid(); + + /** + * Returns the Well-Known Text (WKT) representation of the geospatial object. + * + *

WKT is a human-readable text format for representing geometry data. This provides a + * complement to the binary WKB format, allowing easy inspection and debugging of geospatial data. + * + * @return the WKT string representation + */ + String getWkt(); +} diff --git a/src/main/java/com/databricks/jdbc/api/impl/DatabricksResultSet.java b/src/main/java/com/databricks/jdbc/api/impl/DatabricksResultSet.java index afc2603a56..6b85bfd82c 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/DatabricksResultSet.java +++ b/src/main/java/com/databricks/jdbc/api/impl/DatabricksResultSet.java @@ -1,9 +1,7 @@ package com.databricks.jdbc.api.impl; import static com.databricks.jdbc.common.DatabricksJdbcConstants.EMPTY_STRING; -import static com.databricks.jdbc.common.util.DatabricksTypeUtil.ARRAY; -import static com.databricks.jdbc.common.util.DatabricksTypeUtil.MAP; -import static com.databricks.jdbc.common.util.DatabricksTypeUtil.STRUCT; +import static com.databricks.jdbc.common.util.DatabricksTypeUtil.*; import com.databricks.jdbc.api.IDatabricksResultSet; import com.databricks.jdbc.api.IExecutionStatus; @@ -479,13 +477,19 @@ public ResultSetMetaData getMetaData() throws SQLException { } /** - * Checks if the given type name represents a complex type (ARRAY, MAP, or STRUCT). + * Checks if the given type name represents a complex type (ARRAY, MAP, STRUCT, GEOMETRY, or + * GEOGRAPHY). * * @param typeName The type name to check - * @return true if the type name starts with ARRAY, MAP, or STRUCT, false otherwise + * @return true if the type name starts with ARRAY, MAP, STRUCT, GEOMETRY, or GEOGRAPHY, false + * otherwise */ private static boolean isComplexType(String typeName) { - return typeName.startsWith(ARRAY) || typeName.startsWith(MAP) || typeName.startsWith(STRUCT); + return typeName.startsWith(ARRAY) + || typeName.startsWith(MAP) + || typeName.startsWith(STRUCT) + || typeName.startsWith(GEOMETRY) + || typeName.startsWith(GEOGRAPHY); } @Override @@ -523,6 +527,10 @@ private Object handleComplexDataTypesForSEAInline(Object obj, String columnName) return parser.parseJsonStringToDbMap(obj.toString(), columnName).toString(); } else if (columnName.startsWith(STRUCT)) { return parser.parseJsonStringToDbStruct(obj.toString(), columnName).toString(); + } else if (columnName.startsWith(GEOMETRY)) { + return obj; + } else if (columnName.startsWith(GEOGRAPHY)) { + return obj; } throw new DatabricksParsingException( "Unexpected metadata format. Type is not a COMPLEX: " + columnName, @@ -1961,7 +1969,11 @@ private T getConvertedObject( return defaultValue.get(); } int columnType = resultSetMetaData.getColumnType(columnIndex); - ObjectConverter converter = ConverterHelper.getConverterForSqlType(columnType); + String columnTypeName = resultSetMetaData.getColumnTypeName(columnIndex); + + // Use metadata-aware converter selection for proper handling of databricks-specific types + ObjectConverter converter = + ConverterHelper.getConverterForColumnType(columnType, columnTypeName); return convertMethod.apply(converter, obj); } diff --git a/src/main/java/com/databricks/jdbc/api/impl/DatabricksResultSetMetaData.java b/src/main/java/com/databricks/jdbc/api/impl/DatabricksResultSetMetaData.java index 3afc6e383f..a23d576e15 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/DatabricksResultSetMetaData.java +++ b/src/main/java/com/databricks/jdbc/api/impl/DatabricksResultSetMetaData.java @@ -5,10 +5,7 @@ import static com.databricks.jdbc.common.MetadataResultConstants.LARGE_DISPLAY_COLUMNS; import static com.databricks.jdbc.common.MetadataResultConstants.REMARKS_COLUMN; import static com.databricks.jdbc.common.util.DatabricksThriftUtil.*; -import static com.databricks.jdbc.common.util.DatabricksTypeUtil.TIMESTAMP; -import static com.databricks.jdbc.common.util.DatabricksTypeUtil.TIMESTAMP_NTZ; -import static com.databricks.jdbc.common.util.DatabricksTypeUtil.VARIANT; -import static com.databricks.jdbc.common.util.DatabricksTypeUtil.getBasePrecisionAndScale; +import static com.databricks.jdbc.common.util.DatabricksTypeUtil.*; import com.databricks.jdbc.api.internal.IDatabricksConnectionContext; import com.databricks.jdbc.common.AccessType; @@ -178,6 +175,7 @@ public DatabricksResultSetMetaData( columnIndex < resultManifest.getSchema().getColumnsSize(); columnIndex++) { TColumnDesc columnDesc = resultManifest.getSchema().getColumns().get(columnIndex); + ColumnInfo columnInfo = getColumnInfoFromTColumnDesc(columnDesc); int[] precisionAndScale = getPrecisionAndScale(columnInfo); int precision = precisionAndScale[0]; @@ -205,6 +203,16 @@ public DatabricksResultSetMetaData( .columnTypeClassName("java.lang.String") .columnType(Types.OTHER) .columnTypeText(VARIANT); + } else if (isGeometryColumn(arrowMetadata, columnIndex)) { + columnBuilder + .columnTypeClassName("com.databricks.jdbc.api.impl.DatabricksGeometry") + .columnType(Types.OTHER) + .columnTypeText(GEOMETRY); + } else if (isGeographyColumn(arrowMetadata, columnIndex)) { + columnBuilder + .columnTypeClassName("com.databricks.jdbc.api.impl.DatabricksGeography") + .columnType(Types.OTHER) + .columnTypeText(GEOGRAPHY); } columnsBuilder.add(columnBuilder.build()); columnNameToIndexMap.putIfAbsent(columnInfo.getName(), ++currIndex); @@ -642,6 +650,20 @@ private boolean isVariantColumn(List arrowMetadata, int i) { && arrowMetadata.get(i).equalsIgnoreCase(VARIANT); } + private boolean isGeometryColumn(List arrowMetadata, int i) { + return arrowMetadata != null + && arrowMetadata.size() > i + && arrowMetadata.get(i) != null + && arrowMetadata.get(i).contains(GEOMETRY); + } + + private boolean isGeographyColumn(List arrowMetadata, int i) { + return arrowMetadata != null + && arrowMetadata.size() > i + && arrowMetadata.get(i) != null + && arrowMetadata.get(i).contains(GEOGRAPHY); + } + private ImmutableDatabricksColumn.Builder getColumnBuilder() { return ImmutableDatabricksColumn.builder() .isAutoIncrement(false) diff --git a/src/main/java/com/databricks/jdbc/api/impl/arrow/ArrowStreamResult.java b/src/main/java/com/databricks/jdbc/api/impl/arrow/ArrowStreamResult.java index 29a88fd6ba..c8252b800f 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/arrow/ArrowStreamResult.java +++ b/src/main/java/com/databricks/jdbc/api/impl/arrow/ArrowStreamResult.java @@ -160,7 +160,7 @@ public Object getObject(int columnIndex) throws DatabricksSQLException { } /** - * Checks if the given type is a complex type (ARRAY, MAP, or STRUCT). + * Checks if the given type is a complex type (ARRAY, MAP, STRUCT, GEOMETRY, or GEOGRAPHY). * * @param type The type to check * @return true if the type is a complex type, false otherwise @@ -169,7 +169,9 @@ 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.STRUCT + || type == ColumnInfoTypeName.GEOMETRY + || type == ColumnInfoTypeName.GEOGRAPHY; } /** {@inheritDoc} */ diff --git a/src/main/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverter.java b/src/main/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverter.java index 42be51d2c3..ed169cad54 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverter.java +++ b/src/main/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverter.java @@ -1,10 +1,6 @@ package com.databricks.jdbc.api.impl.converters; -import static com.databricks.jdbc.common.util.DatabricksTypeUtil.ARRAY; -import static com.databricks.jdbc.common.util.DatabricksTypeUtil.MAP; -import static com.databricks.jdbc.common.util.DatabricksTypeUtil.STRUCT; -import static com.databricks.jdbc.common.util.DatabricksTypeUtil.TIMESTAMP; -import static com.databricks.jdbc.common.util.DatabricksTypeUtil.VARIANT; +import static com.databricks.jdbc.common.util.DatabricksTypeUtil.*; import com.databricks.jdbc.api.impl.*; import com.databricks.jdbc.exception.DatabricksParsingException; @@ -25,6 +21,8 @@ import java.util.List; import java.util.Optional; import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.arrow.vector.TimeStampMicroTZVector; import org.apache.arrow.vector.ValueVector; import org.apache.arrow.vector.util.Text; @@ -86,6 +84,12 @@ public static Object convert( if (arrowMetadata.startsWith(TIMESTAMP)) { // for timestamp_ntz column requiredType = ColumnInfoTypeName.TIMESTAMP; } + if (arrowMetadata.startsWith(GEOMETRY)) { + requiredType = ColumnInfoTypeName.GEOMETRY; + } + if (arrowMetadata.startsWith(GEOGRAPHY)) { + requiredType = ColumnInfoTypeName.GEOGRAPHY; + } } if (object == null) { return null; @@ -136,6 +140,9 @@ public static Object convert( } IntervalConverter ic = new IntervalConverter(arrowMetadata); return ic.toLiteral(object); + case GEOMETRY: + case GEOGRAPHY: + return convertToGeospatial(object, arrowMetadata, requiredType); case NULL: return null; default: @@ -163,6 +170,26 @@ private static Object convertToStruct(Object object, String arrowMetadata) return parser.parseJsonStringToDbStruct(object.toString(), arrowMetadata); } + private static AbstractDatabricksGeospatial convertToGeospatial( + Object object, String arrowMetadata, ColumnInfoTypeName type) + throws DatabricksValidationException { + String ewkt = convertToString(object); + // Extract SRID from arrow metadata (format: "GEOMETRY(srid)" or "GEOGRAPHY(srid)") + String typeName = type == ColumnInfoTypeName.GEOMETRY ? GEOMETRY : GEOGRAPHY; + int metadataSrid = extractSRIDFromMetadata(arrowMetadata, typeName); + + // Parse EWKT to extract SRID from data if present + int dataSrid = WKTConverter.extractSRIDFromEWKT(ewkt); + String cleanWkt = WKTConverter.removeSRIDFromEWKT(ewkt); + + // Use SRID from data if available, otherwise from metadata + int finalSrid = (dataSrid != 0) ? dataSrid : metadataSrid; + + return type == ColumnInfoTypeName.GEOMETRY + ? new DatabricksGeometry(cleanWkt, finalSrid) + : new DatabricksGeography(cleanWkt, finalSrid); + } + private static Object convertToTimestamp(Object object, Optional timeZoneOpt) throws DatabricksSQLException { if (object instanceof Text) { @@ -294,4 +321,32 @@ private static T convertToNumber( LOGGER.error(errorMessage); throw new DatabricksValidationException(errorMessage); } + + /** + * Extracts SRID from Arrow metadata string. + * + * @param metadata Arrow metadata like "GEOMETRY(32633)" or "GEOGRAPHY(4326)" + * @param typePrefix The prefix to look for ("GEOMETRY" or "GEOGRAPHY") + * @return SRID value, or 0 if not found + */ + private static int extractSRIDFromMetadata(String metadata, String typePrefix) { + if (metadata == null) { + return 0; + } + + try { + // Look for pattern like "GEOMETRY(32633)" or "GEOGRAPHY(4326)" + String pattern = typePrefix + "\\((\\d+)\\)"; + Pattern p = Pattern.compile(pattern); + Matcher m = p.matcher(metadata); + + if (m.find()) { + return Integer.parseInt(m.group(1)); + } + } catch (Exception e) { + LOGGER.debug("Failed to extract SRID from metadata: {}", metadata, e); + } + + return 0; + } } diff --git a/src/main/java/com/databricks/jdbc/api/impl/converters/ConverterHelper.java b/src/main/java/com/databricks/jdbc/api/impl/converters/ConverterHelper.java index 6815e609f3..42222dd8d5 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/converters/ConverterHelper.java +++ b/src/main/java/com/databricks/jdbc/api/impl/converters/ConverterHelper.java @@ -1,5 +1,10 @@ package com.databricks.jdbc.api.impl.converters; +import static com.databricks.jdbc.common.util.DatabricksTypeUtil.GEOGRAPHY; +import static com.databricks.jdbc.common.util.DatabricksTypeUtil.GEOMETRY; + +import com.databricks.jdbc.api.impl.DatabricksGeography; +import com.databricks.jdbc.api.impl.DatabricksGeometry; import com.databricks.jdbc.exception.DatabricksSQLException; import java.math.BigDecimal; import java.math.BigInteger; @@ -506,6 +511,10 @@ public static Object convertSqlTypeToSpecificJavaType( return converter.toDatabricksArray(obj); } else if (javaType == Struct.class) { return converter.toDatabricksStruct(obj); + } else if (javaType == DatabricksGeometry.class) { + return converter.toDatabricksGeometry(obj); + } else if (javaType == DatabricksGeography.class) { + return converter.toDatabricksGeography(obj); } return converter.toString(obj); // By default, convert to string } @@ -520,6 +529,30 @@ public static ObjectConverter getConverterForSqlType(int columnSqlType) { return CONVERTER_CACHE.getOrDefault(columnSqlType, CONVERTER_CACHE.get(Types.VARCHAR)); } + /** + * Retrieves the appropriate ObjectConverter for a given SQL type and column type name. This + * method provides metadata-aware converter selection, checking the actual column type name first + * for database-specific types before falling back to SQL type-based selection. + * + * @param columnSqlType The SQL type of the column, as defined in java.sql.Types + * @param columnTypeName The actual column type name from metadata (e.g., "GEOMETRY", "GEOGRAPHY") + * @return An ObjectConverter suitable for the specified column type + */ + public static ObjectConverter getConverterForColumnType( + int columnSqlType, String columnTypeName) { + // Check type name first for databricks-specific types + if (columnTypeName != null) { + if (columnTypeName.equals(GEOMETRY) || columnTypeName.equals(GEOGRAPHY)) { + return new GeospatialConverter(); + } + // Future databricks-specific types can be added here + // which are not covered by standard SQL types + } + + // Fall back to SQL type-based selection for standard types + return getConverterForSqlType(columnSqlType); + } + public static boolean isConversionSupported(int fromType, int toType) { return SUPPORTED_CONVERSIONS.containsKey(fromType) && SUPPORTED_CONVERSIONS.get(fromType).contains(toType); diff --git a/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java b/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java new file mode 100644 index 0000000000..82231434f3 --- /dev/null +++ b/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java @@ -0,0 +1,82 @@ +package com.databricks.jdbc.api.impl.converters; + +import com.databricks.jdbc.api.impl.DatabricksGeography; +import com.databricks.jdbc.api.impl.DatabricksGeometry; +import com.databricks.jdbc.api.impl.DatabricksGeospatial; +import com.databricks.jdbc.exception.DatabricksSQLException; +import com.databricks.jdbc.log.JdbcLogger; +import com.databricks.jdbc.log.JdbcLoggerFactory; +import com.databricks.jdbc.model.telemetry.enums.DatabricksDriverErrorCode; +import org.apache.arrow.vector.util.Text; + +public class GeospatialConverter implements ObjectConverter { + + private static final JdbcLogger LOGGER = JdbcLoggerFactory.getLogger(GeospatialConverter.class); + + @Override + public DatabricksGeometry toDatabricksGeometry(Object object) throws DatabricksSQLException { + if (object instanceof DatabricksGeometry) { + return (DatabricksGeometry) object; + } + + if (object instanceof String || object instanceof Text) { + String ewktString = object.toString(); + try { + int srid = WKTConverter.extractSRIDFromEWKT(ewktString); + String cleanWKT = WKTConverter.removeSRIDFromEWKT(ewktString); + return new DatabricksGeometry(cleanWKT, srid); + } catch (Exception e) { + String errorMessage = String.format("Failed to convert EWKT to geometry: %s", ewktString); + LOGGER.warn(errorMessage, e); + throw new DatabricksSQLException(errorMessage, e, DatabricksDriverErrorCode.INVALID_STATE); + } + } + + throw new DatabricksSQLException( + "Unsupported Geometry conversion from type: " + object.getClass(), + DatabricksDriverErrorCode.UNSUPPORTED_OPERATION); + } + + @Override + public DatabricksGeography toDatabricksGeography(Object object) throws DatabricksSQLException { + if (object instanceof DatabricksGeography) { + return (DatabricksGeography) object; + } + + if (object instanceof String || object instanceof Text) { + String ewktString = object.toString(); + try { + int srid = WKTConverter.extractSRIDFromEWKT(ewktString); + String cleanWKT = WKTConverter.removeSRIDFromEWKT(ewktString); + return new DatabricksGeography(cleanWKT, srid); + } catch (Exception e) { + String errorMessage = String.format("Failed to convert EWKT to geography: %s", ewktString); + LOGGER.warn(errorMessage, e); + throw new DatabricksSQLException(errorMessage, e, DatabricksDriverErrorCode.INVALID_STATE); + } + } + + throw new DatabricksSQLException( + "Unsupported Geography conversion from type: " + object.getClass(), + DatabricksDriverErrorCode.UNSUPPORTED_OPERATION); + } + + @Override + public String toString(Object object) throws DatabricksSQLException { + if (object != null) { + return object.toString(); + } + throw new DatabricksSQLException( + "Cannot convert null to String", DatabricksDriverErrorCode.UNSUPPORTED_OPERATION); + } + + @Override + public byte[] toByteArray(Object object) throws DatabricksSQLException { + if (object instanceof DatabricksGeospatial) { + return ((DatabricksGeospatial) object).getWkb(); + } + throw new DatabricksSQLException( + "Unsupported byte array conversion operation for geospatial types", + DatabricksDriverErrorCode.UNSUPPORTED_OPERATION); + } +} diff --git a/src/main/java/com/databricks/jdbc/api/impl/converters/ObjectConverter.java b/src/main/java/com/databricks/jdbc/api/impl/converters/ObjectConverter.java index c1df874bed..ecf8fdc9b6 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/converters/ObjectConverter.java +++ b/src/main/java/com/databricks/jdbc/api/impl/converters/ObjectConverter.java @@ -1,6 +1,8 @@ package com.databricks.jdbc.api.impl.converters; import com.databricks.jdbc.api.impl.DatabricksArray; +import com.databricks.jdbc.api.impl.DatabricksGeography; +import com.databricks.jdbc.api.impl.DatabricksGeometry; import com.databricks.jdbc.api.impl.DatabricksMap; import com.databricks.jdbc.api.impl.DatabricksStruct; import com.databricks.jdbc.exception.DatabricksSQLException; @@ -141,6 +143,24 @@ default DatabricksStruct toDatabricksStruct(Object object) throws DatabricksSQLE "Unsupported Struct conversion operation", DatabricksDriverErrorCode.UNSUPPORTED_OPERATION); } + default DatabricksGeometry toDatabricksGeometry(Object object) throws DatabricksSQLException { + if (object instanceof DatabricksGeometry) { + return (DatabricksGeometry) object; + } + throw new DatabricksSQLException( + "Unsupported Geometry conversion operation", + DatabricksDriverErrorCode.UNSUPPORTED_OPERATION); + } + + default DatabricksGeography toDatabricksGeography(Object object) throws DatabricksSQLException { + if (object instanceof DatabricksGeography) { + return (DatabricksGeography) object; + } + throw new DatabricksSQLException( + "Unsupported Geography conversion operation", + DatabricksDriverErrorCode.UNSUPPORTED_OPERATION); + } + default InputStream toBinaryStream(Object object) throws DatabricksSQLException { try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) { diff --git a/src/main/java/com/databricks/jdbc/api/impl/converters/WKTConverter.java b/src/main/java/com/databricks/jdbc/api/impl/converters/WKTConverter.java new file mode 100644 index 0000000000..4f140badca --- /dev/null +++ b/src/main/java/com/databricks/jdbc/api/impl/converters/WKTConverter.java @@ -0,0 +1,133 @@ +package com.databricks.jdbc.api.impl.converters; + +import com.databricks.jdbc.exception.DatabricksValidationException; +import com.databricks.jdbc.log.JdbcLogger; +import com.databricks.jdbc.log.JdbcLoggerFactory; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKBReader; +import org.locationtech.jts.io.WKBWriter; +import org.locationtech.jts.io.WKTReader; +import org.locationtech.jts.io.WKTWriter; + +/** + * Utility class for converting between WKT (Well-Known Text) and WKB (Well-Known Binary) formats. + * + *

This class uses the JTS (Java Topology Suite) library to provide robust WKT/WKB conversion + * functionality for geospatial data. JTS is a widely-used, well-tested library that implements the + * OpenGIS Consortium's Simple Features Specification for SQL. + */ +public class WKTConverter { + + private static final JdbcLogger LOGGER = JdbcLoggerFactory.getLogger(WKTConverter.class); + + // Thread-safe readers and writers from JTS + private static final WKTReader WKT_READER = new WKTReader(); + private static final WKTWriter WKT_WRITER = new WKTWriter(); + private static final WKBReader WKB_READER = new WKBReader(); + private static final WKBWriter WKB_WRITER = new WKBWriter(); + + /** + * Converts WKT (Well-Known Text) to WKB (Well-Known Binary) format. + * + *

This implementation uses the JTS library to parse the WKT string into a Geometry object and + * then converts it to WKB format. This provides robust, standards-compliant conversion. + * + * @param wkt the WKT string to convert + * @return the WKB representation as a byte array + * @throws DatabricksValidationException if the WKT is invalid + */ + public static byte[] toWKB(String wkt) throws DatabricksValidationException { + if (wkt == null || wkt.trim().isEmpty()) { + throw new DatabricksValidationException("WKT string cannot be null or empty"); + } + + try { + Geometry geometry = WKT_READER.read(wkt); + return WKB_WRITER.write(geometry); + } catch (ParseException e) { + String errorMessage = String.format("Invalid WKT format: %s", wkt); + LOGGER.error(errorMessage, e); + throw new DatabricksValidationException(errorMessage, e); + } + } + + /** + * Converts WKB (Well-Known Binary) to WKT (Well-Known Text) format. + * + *

This implementation uses the JTS library to parse the WKB bytes into a Geometry object and + * then converts it to WKT format. + * + * @param wkb the WKB bytes to convert + * @return the WKT representation as a string + * @throws DatabricksValidationException if the WKB is invalid + */ + public static String toWKT(byte[] wkb) throws DatabricksValidationException { + if (wkb == null || wkb.length == 0) { + throw new DatabricksValidationException("WKB bytes cannot be null or empty"); + } + + try { + Geometry geometry = WKB_READER.read(wkb); + return WKT_WRITER.write(geometry); + } catch (Exception e) { + String errorMessage = String.format("Invalid WKB format: %d bytes", wkb.length); + LOGGER.error(errorMessage, e); + throw new DatabricksValidationException(errorMessage, e); + } + } + + /** + * Extracts the SRID from an EWKT (Extended Well-Known Text) string. + * + *

EWKT format includes SRID prefix: "SRID=4326;POINT(1 2)" + * + * @param ewkt the EWKT string + * @return the SRID value, or 0 if no SRID is specified + */ + public static int extractSRIDFromEWKT(String ewkt) { + if (ewkt == null || ewkt.trim().isEmpty()) { + return 0; + } + + String trimmed = ewkt.trim(); + if (trimmed.startsWith("SRID=")) { + int semicolonIndex = trimmed.indexOf(';'); + if (semicolonIndex > 0) { + try { + String sridStr = trimmed.substring(5, semicolonIndex); + return Integer.parseInt(sridStr); + } catch (NumberFormatException e) { + LOGGER.warn("Invalid SRID format in EWKT: {}", ewkt); + return 0; + } + } + } + + return 0; // Default SRID if not specified + } + + /** + * Removes the SRID prefix from an EWKT string to get clean WKT. + * + *

Converts "SRID=4326;POINT(1 2)" to "POINT(1 2)" + * + * @param ewkt the EWKT string + * @return the clean WKT string without SRID prefix + */ + public static String removeSRIDFromEWKT(String ewkt) { + if (ewkt == null || ewkt.trim().isEmpty()) { + return ewkt; + } + + String trimmed = ewkt.trim(); + if (trimmed.startsWith("SRID=")) { + int semicolonIndex = trimmed.indexOf(';'); + if (semicolonIndex > 0) { + return trimmed.substring(semicolonIndex + 1); + } + } + + return trimmed; // Return as-is if no SRID prefix + } +} diff --git a/src/main/java/com/databricks/jdbc/common/util/DatabricksTypeUtil.java b/src/main/java/com/databricks/jdbc/common/util/DatabricksTypeUtil.java index 755558c8f3..cec1451394 100644 --- a/src/main/java/com/databricks/jdbc/common/util/DatabricksTypeUtil.java +++ b/src/main/java/com/databricks/jdbc/common/util/DatabricksTypeUtil.java @@ -55,6 +55,8 @@ public class DatabricksTypeUtil { public static final String VARIANT = "VARIANT"; public static final String CHAR = "CHAR"; public static final String INTERVAL = "INTERVAL"; + public static final String GEOMETRY = "GEOMETRY"; + public static final String GEOGRAPHY = "GEOGRAPHY"; private static final ArrayList SIGNED_TYPES = new ArrayList<>( Arrays.asList( @@ -146,6 +148,8 @@ public static int getColumnType(ColumnInfoTypeName typeName) { return Types.STRUCT; case ARRAY: return Types.ARRAY; + case GEOMETRY: + case GEOGRAPHY: case USER_DEFINED_TYPE: return Types.OTHER; default: @@ -189,6 +193,10 @@ public static String getColumnTypeClassName(ColumnInfoTypeName typeName) { return "java.sql.Struct"; case ARRAY: return "java.sql.Array"; + case GEOMETRY: + return "com.databricks.jdbc.api.impl.DatabricksGeometry"; + case GEOGRAPHY: + return "com.databricks.jdbc.api.impl.DatabricksGeography"; case NULL: return "null"; case MAP: @@ -251,6 +259,8 @@ public static int getDisplaySize(ColumnInfoTypeName typeName, int precision, int return 4; // Length of `NULL` case ARRAY: case STRUCT: + case GEOMETRY: + case GEOGRAPHY: default: return 255; } diff --git a/src/test/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverterTest.java b/src/test/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverterTest.java index 4afe72d98d..6abd48dd1b 100644 --- a/src/test/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverterTest.java +++ b/src/test/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverterTest.java @@ -2,10 +2,12 @@ import static com.databricks.jdbc.api.impl.converters.ArrowToJavaObjectConverter.convert; import static com.databricks.jdbc.api.impl.converters.ArrowToJavaObjectConverter.getZoneIdFromTimeZoneOpt; -import static com.databricks.jdbc.common.util.DatabricksTypeUtil.VARIANT; +import static com.databricks.jdbc.common.util.DatabricksTypeUtil.*; import static org.junit.jupiter.api.Assertions.*; import com.databricks.jdbc.api.impl.DatabricksArray; +import com.databricks.jdbc.api.impl.DatabricksGeography; +import com.databricks.jdbc.api.impl.DatabricksGeometry; import com.databricks.jdbc.api.impl.DatabricksStruct; import com.databricks.jdbc.api.internal.IDatabricksConnectionContext; import com.databricks.jdbc.exception.DatabricksValidationException; @@ -541,4 +543,80 @@ public void testGetZoneIdFromTimeZoneOpt_InvalidTimeZones() { DateTimeException.class, () -> getZoneIdFromTimeZoneOpt(Optional.of("5:30"))); // Missing sign } + + @Test + public void testGeometryConversion() throws SQLException { + VarCharVector varCharVector = new VarCharVector("varCharVector", this.bufferAllocator); + varCharVector.allocateNew(); + varCharVector.set(0, new Text("POINT(1 2)")); + varCharVector.setValueCount(1); + + Object result = + convert(varCharVector, 0, ColumnInfoTypeName.GEOMETRY, GEOMETRY, new ColumnInfo()); + + assertNotNull(result); + assertInstanceOf(DatabricksGeometry.class, result); + DatabricksGeometry geometry = (DatabricksGeometry) result; + assertEquals("SRID=0;POINT(1 2)", geometry.toString()); + assertEquals(0, geometry.getSrid()); // Default SRID + + varCharVector.close(); + } + + @Test + public void testGeographyConversion() throws SQLException { + VarCharVector varCharVector = new VarCharVector("varCharVector", this.bufferAllocator); + varCharVector.allocateNew(); + varCharVector.set(0, new Text("POINT(1 2)")); + varCharVector.setValueCount(1); + + Object result = + convert(varCharVector, 0, ColumnInfoTypeName.GEOGRAPHY, GEOGRAPHY, new ColumnInfo()); + + assertNotNull(result); + assertInstanceOf(DatabricksGeography.class, result); + DatabricksGeography geography = (DatabricksGeography) result; + assertEquals("SRID=0;POINT(1 2)", geography.toString()); + assertEquals(0, geography.getSrid()); // Default SRID + + varCharVector.close(); + } + + @Test + public void testGeometryWithSRIDConversion() throws SQLException { + VarCharVector varCharVector = new VarCharVector("varCharVector", this.bufferAllocator); + varCharVector.allocateNew(); + varCharVector.set(0, new Text("SRID=4326;POINT(1 2)")); + varCharVector.setValueCount(1); + + Object result = + convert(varCharVector, 0, ColumnInfoTypeName.GEOMETRY, "GEOMETRY", new ColumnInfo()); + + assertNotNull(result); + assertInstanceOf(DatabricksGeometry.class, result); + DatabricksGeometry geometry = (DatabricksGeometry) result; + assertEquals("SRID=4326;POINT(1 2)", geometry.toString()); + assertEquals(4326, geometry.getSrid()); + + varCharVector.close(); + } + + @Test + public void testGeographyWithSRIDConversion() throws SQLException { + VarCharVector varCharVector = new VarCharVector("varCharVector", this.bufferAllocator); + varCharVector.allocateNew(); + varCharVector.set(0, new Text("SRID=4326;POINT(1 2)")); + varCharVector.setValueCount(1); + + Object result = + convert(varCharVector, 0, ColumnInfoTypeName.GEOGRAPHY, "GEOGRAPHY", new ColumnInfo()); + + assertNotNull(result); + assertInstanceOf(DatabricksGeography.class, result); + DatabricksGeography geography = (DatabricksGeography) result; + assertEquals("SRID=4326;POINT(1 2)", geography.toString()); + assertEquals(4326, geography.getSrid()); + + varCharVector.close(); + } } diff --git a/src/test/java/com/databricks/jdbc/api/impl/converters/WKTConverterTest.java b/src/test/java/com/databricks/jdbc/api/impl/converters/WKTConverterTest.java new file mode 100644 index 0000000000..a3b48cfacf --- /dev/null +++ b/src/test/java/com/databricks/jdbc/api/impl/converters/WKTConverterTest.java @@ -0,0 +1,119 @@ +package com.databricks.jdbc.api.impl.converters; + +import static org.junit.jupiter.api.Assertions.*; + +import com.databricks.jdbc.exception.DatabricksValidationException; +import org.junit.jupiter.api.Test; + +/** Test class for WKTConverter utility. */ +public class WKTConverterTest { + + @Test + public void testToWKB_ValidWKT() throws DatabricksValidationException { + String wkt = "POINT(1 2)"; + byte[] wkb = WKTConverter.toWKB(wkt); + + assertNotNull(wkb); + assertTrue(wkb.length > 0); + // WKB is binary data, not UTF-8 bytes of WKT + // We can verify it's valid WKB by converting it back to WKT + String convertedBack = WKTConverter.toWKT(wkb); + // JTS outputs "POINT (1 2)" with a space after POINT, which is valid WKT + assertEquals("POINT (1 2)", convertedBack); + } + + @Test + public void testToWKB_NullWKT() { + assertThrows(DatabricksValidationException.class, () -> WKTConverter.toWKB(null)); + } + + @Test + public void testToWKB_EmptyWKT() { + assertThrows(DatabricksValidationException.class, () -> WKTConverter.toWKB("")); + } + + @Test + public void testToWKB_WhitespaceWKT() { + assertThrows(DatabricksValidationException.class, () -> WKTConverter.toWKB(" ")); + } + + @Test + public void testExtractSRIDFromEWKT_WithSRID() { + String ewkt = "SRID=4326;POINT(1 2)"; + int srid = WKTConverter.extractSRIDFromEWKT(ewkt); + assertEquals(4326, srid); + } + + @Test + public void testExtractSRIDFromEWKT_WithoutSRID() { + String wkt = "POINT(1 2)"; + int srid = WKTConverter.extractSRIDFromEWKT(wkt); + assertEquals(0, srid); + } + + @Test + public void testExtractSRIDFromEWKT_Null() { + int srid = WKTConverter.extractSRIDFromEWKT(null); + assertEquals(0, srid); + } + + @Test + public void testExtractSRIDFromEWKT_Empty() { + int srid = WKTConverter.extractSRIDFromEWKT(""); + assertEquals(0, srid); + } + + @Test + public void testExtractSRIDFromEWKT_InvalidSRID() { + String ewkt = "SRID=invalid;POINT(1 2)"; + int srid = WKTConverter.extractSRIDFromEWKT(ewkt); + assertEquals(0, srid); // Should return 0 for invalid SRID + } + + @Test + public void testExtractSRIDFromEWKT_NoSemicolon() { + String ewkt = "SRID=4326POINT(1 2)"; + int srid = WKTConverter.extractSRIDFromEWKT(ewkt); + assertEquals(0, srid); // Should return 0 if no semicolon + } + + @Test + public void testRemoveSRIDFromEWKT_WithSRID() { + String ewkt = "SRID=4326;POINT(1 2)"; + String wkt = WKTConverter.removeSRIDFromEWKT(ewkt); + assertEquals("POINT(1 2)", wkt); + } + + @Test + public void testRemoveSRIDFromEWKT_WithoutSRID() { + String wkt = "POINT(1 2)"; + String result = WKTConverter.removeSRIDFromEWKT(wkt); + assertEquals("POINT(1 2)", result); + } + + @Test + public void testRemoveSRIDFromEWKT_Null() { + String result = WKTConverter.removeSRIDFromEWKT(null); + assertNull(result); + } + + @Test + public void testRemoveSRIDFromEWKT_Empty() { + String result = WKTConverter.removeSRIDFromEWKT(""); + assertEquals("", result); + } + + @Test + public void testRemoveSRIDFromEWKT_NoSemicolon() { + String ewkt = "SRID=4326POINT(1 2)"; + String result = WKTConverter.removeSRIDFromEWKT(ewkt); + assertEquals("SRID=4326POINT(1 2)", result); // Should return as-is if no semicolon + } + + @Test + public void testRemoveSRIDFromEWKT_OnlySRID() { + String ewkt = "SRID=4326;"; + String result = WKTConverter.removeSRIDFromEWKT(ewkt); + assertEquals("", result); + } +} From 6d2cd466cbc453f3298bc678b64e8c2abf56c36b Mon Sep 17 00:00:00 2001 From: Sreekanth Vadigi Date: Mon, 6 Oct 2025 11:35:36 +0530 Subject: [PATCH 02/14] changelog Signed-off-by: Sreekanth Vadigi --- NEXT_CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index 0e68cbbbe2..44a4f8ff93 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -4,6 +4,7 @@ ### Added - Enabled direct results by default in SEA mode to improve latency for short and small queries. +- Added support for geospatial data types. ### Updated - Telemetry data is now captured more efficiently and consistently due to enhancements in the log and connection close flush logic. - Updated Databricks SDK version to v0.65.0 (This is to fix OAuthClient to properly encode complex query parameters.) From c1b8ce28dcbd3a5bd853db0a99e5134dd9fb61e0 Mon Sep 17 00:00:00 2001 From: Sreekanth Vadigi Date: Tue, 7 Oct 2025 12:59:19 +0530 Subject: [PATCH 03/14] additional unit tests Signed-off-by: Sreekanth Vadigi --- .../api/impl/DatabricksGeographyTest.java | 165 +++++++++++++++++ .../jdbc/api/impl/DatabricksGeometryTest.java | 169 ++++++++++++++++++ .../converters/GeospatialConverterTest.java | 118 ++++++++++++ 3 files changed, 452 insertions(+) create mode 100644 src/test/java/com/databricks/jdbc/api/impl/DatabricksGeographyTest.java create mode 100644 src/test/java/com/databricks/jdbc/api/impl/DatabricksGeometryTest.java create mode 100644 src/test/java/com/databricks/jdbc/api/impl/converters/GeospatialConverterTest.java diff --git a/src/test/java/com/databricks/jdbc/api/impl/DatabricksGeographyTest.java b/src/test/java/com/databricks/jdbc/api/impl/DatabricksGeographyTest.java new file mode 100644 index 0000000000..b1073c02d9 --- /dev/null +++ b/src/test/java/com/databricks/jdbc/api/impl/DatabricksGeographyTest.java @@ -0,0 +1,165 @@ +package com.databricks.jdbc.api.impl; + +import static org.junit.jupiter.api.Assertions.*; + +import com.databricks.jdbc.exception.DatabricksValidationException; +import org.junit.jupiter.api.Test; + +/** Test class for DatabricksGeography. Reference: DatabricksGeometryTest.java */ +public class DatabricksGeographyTest { + + // =================================================================================== + // Constructor tests + // =================================================================================== + + @Test + public void testConstructor_WithValidPoint() throws DatabricksValidationException { + DatabricksGeography geography = new DatabricksGeography("POINT(-122.4194 37.7749)", 0); + assertNotNull(geography); + assertEquals("POINT(-122.4194 37.7749)", geography.getWkt()); + assertEquals(0, geography.getSrid()); + } + + @Test + public void testConstructor_WithValidPointAndSRID() throws DatabricksValidationException { + DatabricksGeography geography = new DatabricksGeography("POINT(-122.4194 37.7749)", 4326); + assertNotNull(geography); + assertEquals("POINT(-122.4194 37.7749)", geography.getWkt()); + assertEquals(4326, geography.getSrid()); + } + + @Test + public void testConstructor_WithLineString() throws DatabricksValidationException { + DatabricksGeography geography = + new DatabricksGeography("LINESTRING(-122.4 37.7, -122.5 37.8, -122.6 37.9)", 4326); + assertNotNull(geography); + assertTrue(geography.getWkt().startsWith("LINESTRING")); + } + + @Test + public void testConstructor_WithPolygon() throws DatabricksValidationException { + DatabricksGeography geography = + new DatabricksGeography( + "POLYGON((-122.4 37.7, -122.5 37.7, -122.5 37.8, -122.4 37.8, -122.4 37.7))", 4326); + assertNotNull(geography); + assertTrue(geography.getWkt().startsWith("POLYGON")); + } + + @Test + public void testConstructor_WithNullWKT_ThrowsException() { + assertThrows(DatabricksValidationException.class, () -> new DatabricksGeography(null, 0)); + } + + @Test + public void testConstructor_WithEmptyWKT_ThrowsException() { + assertThrows(DatabricksValidationException.class, () -> new DatabricksGeography("", 0)); + } + + @Test + public void testConstructor_WithWhitespaceWKT_ThrowsException() { + assertThrows(DatabricksValidationException.class, () -> new DatabricksGeography(" ", 0)); + } + + // =================================================================================== + // Accessor tests + // =================================================================================== + + @Test + public void testGetWkt() throws DatabricksValidationException { + DatabricksGeography geography = new DatabricksGeography("POINT(-122.4194 37.7749)", 4326); + assertEquals("POINT(-122.4194 37.7749)", geography.getWkt()); + } + + @Test + public void testGetSrid_WithZero() throws DatabricksValidationException { + DatabricksGeography geography = new DatabricksGeography("POINT(-122.4194 37.7749)", 0); + assertEquals(0, geography.getSrid()); + } + + @Test + public void testGetSrid_With3857() throws DatabricksValidationException { + DatabricksGeography geography = new DatabricksGeography("POINT(-122.4194 37.7749)", 3857); + assertEquals(3857, geography.getSrid()); + } + + @Test + public void testGetWkb_ReturnsValidBytes() throws DatabricksValidationException { + DatabricksGeography geography = new DatabricksGeography("POINT(-122.4194 37.7749)", 4326); + byte[] wkb = geography.getWkb(); + assertNotNull(wkb); + assertTrue(wkb.length > 0); + } + + // =================================================================================== + // toString tests + // =================================================================================== + + @Test + public void testToString_WithZeroSRID() throws DatabricksValidationException { + DatabricksGeography geography = new DatabricksGeography("POINT(-122.4194 37.7749)", 0); + assertEquals("SRID=0;POINT(-122.4194 37.7749)", geography.toString()); + } + + @Test + public void testToString_With4326() throws DatabricksValidationException { + DatabricksGeography geography = new DatabricksGeography("POINT(-122.4194 37.7749)", 4326); + assertEquals("SRID=4326;POINT(-122.4194 37.7749)", geography.toString()); + } + + // =================================================================================== + // equals and hashCode tests + // =================================================================================== + + @Test + public void testEquals_SameWKTAndSRID() throws DatabricksValidationException { + DatabricksGeography geo1 = new DatabricksGeography("POINT(-122.4194 37.7749)", 4326); + DatabricksGeography geo2 = new DatabricksGeography("POINT(-122.4194 37.7749)", 4326); + assertEquals(geo1, geo2); + } + + @Test + public void testEquals_DifferentWKT() throws DatabricksValidationException { + DatabricksGeography geo1 = new DatabricksGeography("POINT(-122.4194 37.7749)", 4326); + DatabricksGeography geo2 = new DatabricksGeography("POINT(-118.2437 34.0522)", 4326); + assertNotEquals(geo1, geo2); + } + + @Test + public void testEquals_DifferentSRID() throws DatabricksValidationException { + DatabricksGeography geo1 = new DatabricksGeography("POINT(-122.4194 37.7749)", 4326); + DatabricksGeography geo2 = new DatabricksGeography("POINT(-122.4194 37.7749)", 3857); + assertNotEquals(geo1, geo2); + } + + @Test + public void testEquals_SameInstance() throws DatabricksValidationException { + DatabricksGeography geography = new DatabricksGeography("POINT(-122.4194 37.7749)", 4326); + assertEquals(geography, geography); + } + + @Test + public void testEquals_WithNull() throws DatabricksValidationException { + DatabricksGeography geography = new DatabricksGeography("POINT(-122.4194 37.7749)", 4326); + assertNotEquals(geography, null); + } + + @Test + public void testEquals_WithDifferentClass() throws DatabricksValidationException { + DatabricksGeography geography = new DatabricksGeography("POINT(-122.4194 37.7749)", 4326); + assertNotEquals(geography, "POINT(-122.4194 37.7749)"); + } + + @Test + public void testHashCode_EqualObjects() throws DatabricksValidationException { + DatabricksGeography geo1 = new DatabricksGeography("POINT(-122.4194 37.7749)", 4326); + DatabricksGeography geo2 = new DatabricksGeography("POINT(-122.4194 37.7749)", 4326); + assertEquals(geo1.hashCode(), geo2.hashCode()); + } + + @Test + public void testHashCode_DifferentObjects() throws DatabricksValidationException { + DatabricksGeography geo1 = new DatabricksGeography("POINT(-122.4194 37.7749)", 4326); + DatabricksGeography geo2 = new DatabricksGeography("POINT(-118.2437 34.0522)", 4326); + assertNotEquals(geo1.hashCode(), geo2.hashCode()); + } +} diff --git a/src/test/java/com/databricks/jdbc/api/impl/DatabricksGeometryTest.java b/src/test/java/com/databricks/jdbc/api/impl/DatabricksGeometryTest.java new file mode 100644 index 0000000000..4c148df219 --- /dev/null +++ b/src/test/java/com/databricks/jdbc/api/impl/DatabricksGeometryTest.java @@ -0,0 +1,169 @@ +package com.databricks.jdbc.api.impl; + +import static org.junit.jupiter.api.Assertions.*; + +import com.databricks.jdbc.exception.DatabricksValidationException; +import org.junit.jupiter.api.Test; + +/** Test class for DatabricksGeometry. Reference: DatabricksArrayTest.java */ +public class DatabricksGeometryTest { + + // =================================================================================== + // Constructor tests + // =================================================================================== + + @Test + public void testConstructor_WithValidPoint() throws DatabricksValidationException { + DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 0); + assertNotNull(geometry); + assertEquals("POINT(1 2)", geometry.getWkt()); + assertEquals(0, geometry.getSrid()); + } + + @Test + public void testConstructor_WithValidPointAndSRID() throws DatabricksValidationException { + DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 4326); + assertNotNull(geometry); + assertEquals("POINT(1 2)", geometry.getWkt()); + assertEquals(4326, geometry.getSrid()); + } + + @Test + public void testConstructor_WithLineString() throws DatabricksValidationException { + DatabricksGeometry geometry = new DatabricksGeometry("LINESTRING(0 0, 10 10, 20 20)", 0); + assertNotNull(geometry); + assertTrue(geometry.getWkt().startsWith("LINESTRING")); + } + + @Test + public void testConstructor_WithPolygon() throws DatabricksValidationException { + DatabricksGeometry geometry = + new DatabricksGeometry("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))", 0); + assertNotNull(geometry); + assertTrue(geometry.getWkt().startsWith("POLYGON")); + } + + @Test + public void testConstructor_WithNullWKT_ThrowsException() { + assertThrows(DatabricksValidationException.class, () -> new DatabricksGeometry(null, 0)); + } + + @Test + public void testConstructor_WithEmptyWKT_ThrowsException() { + assertThrows(DatabricksValidationException.class, () -> new DatabricksGeometry("", 0)); + } + + @Test + public void testConstructor_WithWhitespaceWKT_ThrowsException() { + assertThrows(DatabricksValidationException.class, () -> new DatabricksGeometry(" ", 0)); + } + + // =================================================================================== + // Accessor tests + // =================================================================================== + + @Test + public void testGetWkt() throws DatabricksValidationException { + DatabricksGeometry geometry = new DatabricksGeometry("POINT(5 10)", 4326); + assertEquals("POINT(5 10)", geometry.getWkt()); + } + + @Test + public void testGetSrid_WithZero() throws DatabricksValidationException { + DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 0); + assertEquals(0, geometry.getSrid()); + } + + @Test + public void testGetSrid_With4326() throws DatabricksValidationException { + DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 4326); + assertEquals(4326, geometry.getSrid()); + } + + @Test + public void testGetSrid_With3857() throws DatabricksValidationException { + DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 3857); + assertEquals(3857, geometry.getSrid()); + } + + @Test + public void testGetWkb_ReturnsValidBytes() throws DatabricksValidationException { + DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 4326); + byte[] wkb = geometry.getWkb(); + assertNotNull(wkb); + assertTrue(wkb.length > 0); + } + + // =================================================================================== + // toString tests + // =================================================================================== + + @Test + public void testToString_WithZeroSRID() throws DatabricksValidationException { + DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 0); + assertEquals("SRID=0;POINT(1 2)", geometry.toString()); + } + + @Test + public void testToString_With4326() throws DatabricksValidationException { + DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 4326); + assertEquals("SRID=4326;POINT(1 2)", geometry.toString()); + } + + // =================================================================================== + // equals and hashCode tests + // =================================================================================== + + @Test + public void testEquals_SameWKTAndSRID() throws DatabricksValidationException { + DatabricksGeometry geo1 = new DatabricksGeometry("POINT(1 2)", 4326); + DatabricksGeometry geo2 = new DatabricksGeometry("POINT(1 2)", 4326); + assertEquals(geo1, geo2); + } + + @Test + public void testEquals_DifferentWKT() throws DatabricksValidationException { + DatabricksGeometry geo1 = new DatabricksGeometry("POINT(1 2)", 4326); + DatabricksGeometry geo2 = new DatabricksGeometry("POINT(3 4)", 4326); + assertNotEquals(geo1, geo2); + } + + @Test + public void testEquals_DifferentSRID() throws DatabricksValidationException { + DatabricksGeometry geo1 = new DatabricksGeometry("POINT(1 2)", 4326); + DatabricksGeometry geo2 = new DatabricksGeometry("POINT(1 2)", 3857); + assertNotEquals(geo1, geo2); + } + + @Test + public void testEquals_SameInstance() throws DatabricksValidationException { + DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 4326); + assertEquals(geometry, geometry); + } + + @Test + public void testEquals_WithNull() throws DatabricksValidationException { + DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 4326); + assertNotEquals(geometry, null); + } + + @Test + public void testEquals_WithDifferentClass() throws DatabricksValidationException { + DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 4326); + assertNotEquals(geometry, "POINT(1 2)"); + } + + @Test + public void testHashCode_EqualObjects() throws DatabricksValidationException { + DatabricksGeometry geo1 = new DatabricksGeometry("POINT(1 2)", 4326); + DatabricksGeometry geo2 = new DatabricksGeometry("POINT(1 2)", 4326); + assertEquals(geo1.hashCode(), geo2.hashCode()); + } + + @Test + public void testHashCode_DifferentObjects() throws DatabricksValidationException { + DatabricksGeometry geo1 = new DatabricksGeometry("POINT(1 2)", 4326); + DatabricksGeometry geo2 = new DatabricksGeometry("POINT(3 4)", 4326); + assertNotEquals(geo1.hashCode(), geo2.hashCode()); + } +} diff --git a/src/test/java/com/databricks/jdbc/api/impl/converters/GeospatialConverterTest.java b/src/test/java/com/databricks/jdbc/api/impl/converters/GeospatialConverterTest.java new file mode 100644 index 0000000000..3a54158190 --- /dev/null +++ b/src/test/java/com/databricks/jdbc/api/impl/converters/GeospatialConverterTest.java @@ -0,0 +1,118 @@ +package com.databricks.jdbc.api.impl.converters; + +import static org.junit.jupiter.api.Assertions.*; + +import com.databricks.jdbc.api.impl.DatabricksGeography; +import com.databricks.jdbc.api.impl.DatabricksGeometry; +import com.databricks.jdbc.exception.DatabricksSQLException; +import org.apache.arrow.vector.util.Text; +import org.junit.jupiter.api.Test; + +/** Test class for GeospatialConverter. */ +public class GeospatialConverterTest { + + private final GeospatialConverter converter = new GeospatialConverter(); + + // =================================================================================== + // toDatabricksGeometry() tests + // =================================================================================== + + @Test + public void testToDatabricksGeometry_WithValidWKT() throws DatabricksSQLException { + DatabricksGeometry result = converter.toDatabricksGeometry("POINT(1 2)"); + assertNotNull(result); + assertEquals("POINT(1 2)", result.getWkt()); + assertEquals(0, result.getSrid()); + } + + @Test + public void testToDatabricksGeometry_WithEWKT() throws DatabricksSQLException { + DatabricksGeometry result = converter.toDatabricksGeometry("SRID=4326;POINT(1 2)"); + assertNotNull(result); + assertEquals("POINT(1 2)", result.getWkt()); + assertEquals(4326, result.getSrid()); + } + + @Test + public void testToDatabricksGeometry_WithTextInput() throws DatabricksSQLException { + Text textInput = new Text("POINT(1 2)"); + DatabricksGeometry result = converter.toDatabricksGeometry(textInput); + assertNotNull(result); + assertEquals("POINT(1 2)", result.getWkt()); + } + + @Test + public void testToDatabricksGeometry_WithGeometryInstance_ReturnsItself() + throws DatabricksSQLException { + DatabricksGeometry input = new DatabricksGeometry("POINT(1 2)", 4326); + DatabricksGeometry result = converter.toDatabricksGeometry(input); + assertSame(input, result); + } + + @Test + public void testToDatabricksGeometry_WithUnsupportedType_ThrowsException() { + assertThrows(DatabricksSQLException.class, () -> converter.toDatabricksGeometry(123)); + } + + // =================================================================================== + // toDatabricksGeography() tests + // =================================================================================== + + @Test + public void testToDatabricksGeography_WithValidWKT() throws DatabricksSQLException { + DatabricksGeography result = converter.toDatabricksGeography("POINT(-122.4194 37.7749)"); + assertNotNull(result); + assertEquals("POINT(-122.4194 37.7749)", result.getWkt()); + assertEquals(0, result.getSrid()); + } + + @Test + public void testToDatabricksGeography_WithEWKT() throws DatabricksSQLException { + DatabricksGeography result = + converter.toDatabricksGeography("SRID=4326;POINT(-122.4194 37.7749)"); + assertNotNull(result); + assertEquals("POINT(-122.4194 37.7749)", result.getWkt()); + assertEquals(4326, result.getSrid()); + } + + @Test + public void testToDatabricksGeography_WithGeographyInstance_ReturnsItself() + throws DatabricksSQLException { + DatabricksGeography input = new DatabricksGeography("POINT(1 2)", 4326); + DatabricksGeography result = converter.toDatabricksGeography(input); + assertSame(input, result); + } + + // =================================================================================== + // toString() tests + // =================================================================================== + + @Test + public void testToString_WithGeometryObject() throws DatabricksSQLException { + DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 4326); + String result = converter.toString(geometry); + assertEquals("SRID=4326;POINT(1 2)", result); + } + + @Test + public void testToString_WithNull_ThrowsException() { + assertThrows(DatabricksSQLException.class, () -> converter.toString(null)); + } + + // =================================================================================== + // toByteArray() tests + // =================================================================================== + + @Test + public void testToByteArray_WithGeometryObject() throws DatabricksSQLException { + DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 4326); + byte[] result = converter.toByteArray(geometry); + assertNotNull(result); + assertTrue(result.length > 0); + } + + @Test + public void testToByteArray_WithNonGeospatialType_ThrowsException() { + assertThrows(DatabricksSQLException.class, () -> converter.toByteArray("not geospatial")); + } +} From e93c3f86b0d042c1bf9a905f327d6488332574f3 Mon Sep 17 00:00:00 2001 From: Sreekanth Vadigi Date: Tue, 7 Oct 2025 20:37:46 +0530 Subject: [PATCH 04/14] addressing review comments Signed-off-by: Sreekanth Vadigi --- NEXT_CHANGELOG.md | 2 +- pom.xml | 4 ++++ .../api/impl/DatabricksResultSetMetaData.java | 16 ++++++++-------- .../api/impl/converters/ConverterHelper.java | 5 ----- .../jdbc/common/util/DatabricksTypeUtil.java | 8 ++++++-- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index c8066a7803..03698278e8 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -3,8 +3,8 @@ ## [Unreleased] ### Added -- Enabled direct results by default in SEA mode to improve latency for short and small queries. - Added support for geospatial data types. + ### Updated ### Fixed diff --git a/pom.xml b/pom.xml index 85e121e5bf..52cd2961d7 100644 --- a/pom.xml +++ b/pom.xml @@ -544,6 +544,10 @@ org.json com.databricks.internal.json + + org.locationtech.jts + com.databricks.internal.jts + org.osgi com.databricks.internal.osgi diff --git a/src/main/java/com/databricks/jdbc/api/impl/DatabricksResultSetMetaData.java b/src/main/java/com/databricks/jdbc/api/impl/DatabricksResultSetMetaData.java index a23d576e15..96d831aaa6 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/DatabricksResultSetMetaData.java +++ b/src/main/java/com/databricks/jdbc/api/impl/DatabricksResultSetMetaData.java @@ -650,18 +650,18 @@ private boolean isVariantColumn(List arrowMetadata, int i) { && arrowMetadata.get(i).equalsIgnoreCase(VARIANT); } - private boolean isGeometryColumn(List arrowMetadata, int i) { + private boolean isGeometryColumn(List arrowMetadata, int index) { return arrowMetadata != null - && arrowMetadata.size() > i - && arrowMetadata.get(i) != null - && arrowMetadata.get(i).contains(GEOMETRY); + && arrowMetadata.size() > index + && arrowMetadata.get(index) != null + && arrowMetadata.get(index).contains(GEOMETRY); } - private boolean isGeographyColumn(List arrowMetadata, int i) { + private boolean isGeographyColumn(List arrowMetadata, int index) { return arrowMetadata != null - && arrowMetadata.size() > i - && arrowMetadata.get(i) != null - && arrowMetadata.get(i).contains(GEOGRAPHY); + && arrowMetadata.size() > index + && arrowMetadata.get(index) != null + && arrowMetadata.get(index).contains(GEOGRAPHY); } private ImmutableDatabricksColumn.Builder getColumnBuilder() { diff --git a/src/main/java/com/databricks/jdbc/api/impl/converters/ConverterHelper.java b/src/main/java/com/databricks/jdbc/api/impl/converters/ConverterHelper.java index 42222dd8d5..c17a90d807 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/converters/ConverterHelper.java +++ b/src/main/java/com/databricks/jdbc/api/impl/converters/ConverterHelper.java @@ -540,16 +540,11 @@ public static ObjectConverter getConverterForSqlType(int columnSqlType) { */ public static ObjectConverter getConverterForColumnType( int columnSqlType, String columnTypeName) { - // Check type name first for databricks-specific types if (columnTypeName != null) { if (columnTypeName.equals(GEOMETRY) || columnTypeName.equals(GEOGRAPHY)) { return new GeospatialConverter(); } - // Future databricks-specific types can be added here - // which are not covered by standard SQL types } - - // Fall back to SQL type-based selection for standard types return getConverterForSqlType(columnSqlType); } diff --git a/src/main/java/com/databricks/jdbc/common/util/DatabricksTypeUtil.java b/src/main/java/com/databricks/jdbc/common/util/DatabricksTypeUtil.java index cec1451394..a0323df30f 100644 --- a/src/main/java/com/databricks/jdbc/common/util/DatabricksTypeUtil.java +++ b/src/main/java/com/databricks/jdbc/common/util/DatabricksTypeUtil.java @@ -57,6 +57,10 @@ public class DatabricksTypeUtil { public static final String INTERVAL = "INTERVAL"; public static final String GEOMETRY = "GEOMETRY"; public static final String GEOGRAPHY = "GEOGRAPHY"; + public static final String GEOMETRY_CLASS_NAME = + "com.databricks.jdbc.api.impl.DatabricksGeometry"; + public static final String GEOGRAPHY_CLASS_NAME = + "com.databricks.jdbc.api.impl.DatabricksGeography"; private static final ArrayList SIGNED_TYPES = new ArrayList<>( Arrays.asList( @@ -194,9 +198,9 @@ public static String getColumnTypeClassName(ColumnInfoTypeName typeName) { case ARRAY: return "java.sql.Array"; case GEOMETRY: - return "com.databricks.jdbc.api.impl.DatabricksGeometry"; + return GEOMETRY_CLASS_NAME; case GEOGRAPHY: - return "com.databricks.jdbc.api.impl.DatabricksGeography"; + return GEOGRAPHY_CLASS_NAME; case NULL: return "null"; case MAP: From e911082182dad5ac7615a208bf2fbb27d56cf067 Mon Sep 17 00:00:00 2001 From: Sreekanth Vadigi Date: Wed, 8 Oct 2025 19:42:33 +0530 Subject: [PATCH 05/14] addressing review comments - 2 Signed-off-by: Sreekanth Vadigi --- .../api/impl/DatabricksResultSetMetaData.java | 4 ++-- .../ArrowToJavaObjectConverter.java | 22 ++++++++++++------- .../api/impl/converters/ConverterHelper.java | 3 ++- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/databricks/jdbc/api/impl/DatabricksResultSetMetaData.java b/src/main/java/com/databricks/jdbc/api/impl/DatabricksResultSetMetaData.java index 96d831aaa6..6836257872 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/DatabricksResultSetMetaData.java +++ b/src/main/java/com/databricks/jdbc/api/impl/DatabricksResultSetMetaData.java @@ -205,12 +205,12 @@ public DatabricksResultSetMetaData( .columnTypeText(VARIANT); } else if (isGeometryColumn(arrowMetadata, columnIndex)) { columnBuilder - .columnTypeClassName("com.databricks.jdbc.api.impl.DatabricksGeometry") + .columnTypeClassName(GEOMETRY_CLASS_NAME) .columnType(Types.OTHER) .columnTypeText(GEOMETRY); } else if (isGeographyColumn(arrowMetadata, columnIndex)) { columnBuilder - .columnTypeClassName("com.databricks.jdbc.api.impl.DatabricksGeography") + .columnTypeClassName(GEOGRAPHY_CLASS_NAME) .columnType(Types.OTHER) .columnTypeText(GEOGRAPHY); } diff --git a/src/main/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverter.java b/src/main/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverter.java index ed169cad54..634977c803 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverter.java +++ b/src/main/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverter.java @@ -30,6 +30,11 @@ public class ArrowToJavaObjectConverter { private static final JdbcLogger LOGGER = JdbcLoggerFactory.getLogger(ArrowToJavaObjectConverter.class); + + // Pre-compiled patterns for SRID extraction from metadata + private static final Pattern GEOMETRY_SRID_PATTERN = Pattern.compile("GEOMETRY\\((\\d+)\\)"); + private static final Pattern GEOGRAPHY_SRID_PATTERN = Pattern.compile("GEOGRAPHY\\((\\d+)\\)"); + private static final List DATE_FORMATTERS = Arrays.asList( DateTimeFormatter.ofPattern("yyyy-MM-dd"), @@ -174,16 +179,17 @@ private static AbstractDatabricksGeospatial convertToGeospatial( Object object, String arrowMetadata, ColumnInfoTypeName type) throws DatabricksValidationException { String ewkt = convertToString(object); - // Extract SRID from arrow metadata (format: "GEOMETRY(srid)" or "GEOGRAPHY(srid)") - String typeName = type == ColumnInfoTypeName.GEOMETRY ? GEOMETRY : GEOGRAPHY; - int metadataSrid = extractSRIDFromMetadata(arrowMetadata, typeName); // Parse EWKT to extract SRID from data if present int dataSrid = WKTConverter.extractSRIDFromEWKT(ewkt); String cleanWkt = WKTConverter.removeSRIDFromEWKT(ewkt); - // Use SRID from data if available, otherwise from metadata - int finalSrid = (dataSrid != 0) ? dataSrid : metadataSrid; + // Extract SRID from metadata if not present in data + int finalSrid = dataSrid; + if (dataSrid == 0) { + String typeName = type == ColumnInfoTypeName.GEOMETRY ? GEOMETRY : GEOGRAPHY; + finalSrid = extractSRIDFromMetadata(arrowMetadata, typeName); + } return type == ColumnInfoTypeName.GEOMETRY ? new DatabricksGeometry(cleanWkt, finalSrid) @@ -336,9 +342,9 @@ private static int extractSRIDFromMetadata(String metadata, String typePrefix) { try { // Look for pattern like "GEOMETRY(32633)" or "GEOGRAPHY(4326)" - String pattern = typePrefix + "\\((\\d+)\\)"; - Pattern p = Pattern.compile(pattern); - Matcher m = p.matcher(metadata); + Pattern pattern = + typePrefix.equals(GEOMETRY) ? GEOMETRY_SRID_PATTERN : GEOGRAPHY_SRID_PATTERN; + Matcher m = pattern.matcher(metadata); if (m.find()) { return Integer.parseInt(m.group(1)); diff --git a/src/main/java/com/databricks/jdbc/api/impl/converters/ConverterHelper.java b/src/main/java/com/databricks/jdbc/api/impl/converters/ConverterHelper.java index c17a90d807..d9b019bc78 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/converters/ConverterHelper.java +++ b/src/main/java/com/databricks/jdbc/api/impl/converters/ConverterHelper.java @@ -17,6 +17,7 @@ public class ConverterHelper { private static final Map CONVERTER_CACHE = new HashMap<>(); private static final Map> SUPPORTED_CONVERSIONS = new HashMap<>(); + private static final GeospatialConverter GEOSPATIAL_CONVERTER = new GeospatialConverter(); static { // Numeric Types @@ -542,7 +543,7 @@ public static ObjectConverter getConverterForColumnType( int columnSqlType, String columnTypeName) { if (columnTypeName != null) { if (columnTypeName.equals(GEOMETRY) || columnTypeName.equals(GEOGRAPHY)) { - return new GeospatialConverter(); + return GEOSPATIAL_CONVERTER; } } return getConverterForSqlType(columnSqlType); From 2a7e20abdf20aa89633d6d4a8feee7a3df322561 Mon Sep 17 00:00:00 2001 From: Sreekanth Vadigi Date: Fri, 10 Oct 2025 14:10:05 +0530 Subject: [PATCH 06/14] thread safe wkt converter Signed-off-by: Sreekanth Vadigi --- .../api/impl/converters/WKTConverter.java | 17 ++++--- .../api/impl/converters/WKTConverterTest.java | 48 +++++++++++++++++++ 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/databricks/jdbc/api/impl/converters/WKTConverter.java b/src/main/java/com/databricks/jdbc/api/impl/converters/WKTConverter.java index 4f140badca..52ccae52d5 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/converters/WKTConverter.java +++ b/src/main/java/com/databricks/jdbc/api/impl/converters/WKTConverter.java @@ -21,11 +21,10 @@ public class WKTConverter { private static final JdbcLogger LOGGER = JdbcLoggerFactory.getLogger(WKTConverter.class); - // Thread-safe readers and writers from JTS - private static final WKTReader WKT_READER = new WKTReader(); - private static final WKTWriter WKT_WRITER = new WKTWriter(); - private static final WKBReader WKB_READER = new WKBReader(); - private static final WKBWriter WKB_WRITER = new WKBWriter(); + private static final ThreadLocal WKT_READER = ThreadLocal.withInitial(WKTReader::new); + private static final ThreadLocal WKT_WRITER = ThreadLocal.withInitial(WKTWriter::new); + private static final ThreadLocal WKB_READER = ThreadLocal.withInitial(WKBReader::new); + private static final ThreadLocal WKB_WRITER = ThreadLocal.withInitial(WKBWriter::new); /** * Converts WKT (Well-Known Text) to WKB (Well-Known Binary) format. @@ -43,8 +42,8 @@ public static byte[] toWKB(String wkt) throws DatabricksValidationException { } try { - Geometry geometry = WKT_READER.read(wkt); - return WKB_WRITER.write(geometry); + Geometry geometry = WKT_READER.get().read(wkt); + return WKB_WRITER.get().write(geometry); } catch (ParseException e) { String errorMessage = String.format("Invalid WKT format: %s", wkt); LOGGER.error(errorMessage, e); @@ -68,8 +67,8 @@ public static String toWKT(byte[] wkb) throws DatabricksValidationException { } try { - Geometry geometry = WKB_READER.read(wkb); - return WKT_WRITER.write(geometry); + Geometry geometry = WKB_READER.get().read(wkb); + return WKT_WRITER.get().write(geometry); } catch (Exception e) { String errorMessage = String.format("Invalid WKB format: %d bytes", wkb.length); LOGGER.error(errorMessage, e); diff --git a/src/test/java/com/databricks/jdbc/api/impl/converters/WKTConverterTest.java b/src/test/java/com/databricks/jdbc/api/impl/converters/WKTConverterTest.java index a3b48cfacf..feb29ca7a2 100644 --- a/src/test/java/com/databricks/jdbc/api/impl/converters/WKTConverterTest.java +++ b/src/test/java/com/databricks/jdbc/api/impl/converters/WKTConverterTest.java @@ -3,6 +3,13 @@ import static org.junit.jupiter.api.Assertions.*; import com.databricks.jdbc.exception.DatabricksValidationException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; /** Test class for WKTConverter utility. */ @@ -116,4 +123,45 @@ public void testRemoveSRIDFromEWKT_OnlySRID() { String result = WKTConverter.removeSRIDFromEWKT(ewkt); assertEquals("", result); } + + @Test + public void testConcurrency() throws Exception { + int numThreads = 50; + ExecutorService executor = Executors.newFixedThreadPool(numThreads); + CountDownLatch latch = new CountDownLatch(numThreads); + List> futures = new ArrayList<>(); + + for (int threadNum = 0; threadNum < numThreads; threadNum++) { + final int threadId = threadNum; + Future future = + executor.submit( + () -> { + try { + int x = threadId; + int y = threadId * 2; + String wkt = String.format("POINT (%d %d)", x, y); + + byte[] wkb = WKTConverter.toWKB(wkt); + assertNotNull(wkb, "Thread " + threadId + ": WKB should not be null"); + + String convertedWkt = WKTConverter.toWKT(wkb); + assertEquals(wkt, convertedWkt, "Thread " + threadId + ": WKT mismatch"); + + } catch (Exception e) { + fail("Thread " + threadId + " failed: " + e.getMessage()); + } finally { + latch.countDown(); + } + }); + futures.add(future); + } + + assertTrue(latch.await(10, TimeUnit.SECONDS), "Not all threads completed in time"); + executor.shutdown(); + + // Verify all threads completed successfully + for (Future future : futures) { + future.get(); + } + } } From 4aad99d82f20896d2cfb3bd92d74c73bd4d37872 Mon Sep 17 00:00:00 2001 From: Sreekanth Vadigi Date: Fri, 10 Oct 2025 15:11:35 +0530 Subject: [PATCH 07/14] geospatial fake service test Signed-off-by: Sreekanth Vadigi --- .../tests/DataTypesIntegrationTests.java | 52 +++++++++++++++++++ ...-05a66b02-c06d-4941-9b01-e820827c2f85.json | 25 +++++++++ ...-26c29504-a4b8-440b-bbd1-d7b1d3576eb7.json | 38 ++++++++++++++ ...-53ba8b00-9e82-4ffe-a90a-aa18dfea3f89.json | 37 +++++++++++++ ...-423765cb-ebcc-4198-8b3e-2c1997822b1c.json | 29 +++++++++++ ...-838649f2-ff8b-416a-b28f-e9bef5a36f7e.json | 29 +++++++++++ ...-1bc97f2a-5ba2-48a6-a701-819fd4d1fd53.json | 35 +++++++++++++ ...-03a6959b-2f46-41af-9cc0-dfd458a4d0ac.json | 29 +++++++++++ ...-75d1e2b1-4917-4fc3-bc23-63e9201f71c2.json | 36 +++++++++++++ ...-872f82e0-1642-4fc5-a033-23dc902aed10.json | 35 +++++++++++++ 10 files changed, 345 insertions(+) create mode 100644 src/test/resources/cloudfetchapi/datatypesintegrationtests/testgeospatialtypes/mappings/oregon-staging_6051921418418893.jobs_sql_2025-10-10_09_results_2025-10-10t093531z_dfb38243-a074-4425-a7fe-772f480f384a-05a66b02-c06d-4941-9b01-e820827c2f85.json create mode 100644 src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_sessions-26c29504-a4b8-440b-bbd1-d7b1d3576eb7.json create mode 100644 src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_sessions-53ba8b00-9e82-4ffe-a90a-aa18dfea3f89.json create mode 100644 src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_sessions_01f0a5bc-7472-14b8-bc16-71f68ce36d0c-423765cb-ebcc-4198-8b3e-2c1997822b1c.json create mode 100644 src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_sessions_01f0a5bc-75de-14b1-aba0-dd791b1a50e3-838649f2-ff8b-416a-b28f-e9bef5a36f7e.json create mode 100644 src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_statements-1bc97f2a-5ba2-48a6-a701-819fd4d1fd53.json create mode 100644 src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_statements_01f0a5bc-7691-1333-a867-e256984f11cf-03a6959b-2f46-41af-9cc0-dfd458a4d0ac.json create mode 100644 src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/oidc_.well-known_oauth-authorization-server-75d1e2b1-4917-4fc3-bc23-63e9201f71c2.json create mode 100644 src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/oidc_.well-known_oauth-authorization-server-872f82e0-1642-4fc5-a033-23dc902aed10.json diff --git a/src/test/java/com/databricks/jdbc/integration/fakeservice/tests/DataTypesIntegrationTests.java b/src/test/java/com/databricks/jdbc/integration/fakeservice/tests/DataTypesIntegrationTests.java index 684a478a5a..f4a03c6f44 100644 --- a/src/test/java/com/databricks/jdbc/integration/fakeservice/tests/DataTypesIntegrationTests.java +++ b/src/test/java/com/databricks/jdbc/integration/fakeservice/tests/DataTypesIntegrationTests.java @@ -338,6 +338,58 @@ private void validateIntervalResults(ResultSet resultSet) throws SQLException { assertFalse(resultSet.next()); } + @Test + void testGeospatialTypes() throws SQLException { + String query = + "SELECT * FROM (VALUES " + + "(1, ST_GeomFromText('POINT (1 2)'), ST_GeogFromText('POINT (3 4)')), " + + "(2, ST_GeomFromText('LINESTRING (0 0, 1 1, 2 2)'), ST_GeogFromText('LINESTRING (5 5, 6 6)')), " + + "(3, NULL, NULL)" + + ") AS geospatial_data(id, geom, geog) " + + "ORDER BY id"; + + ResultSet rs = executeQuery(connection, query); + assertNotNull( + rs, "ResultSet should not be null - GEOMETRY/GEOGRAPHY types may not be supported"); + ResultSetMetaData rsmd = rs.getMetaData(); + + // Validate metadata + assertEquals("GEOMETRY", rsmd.getColumnTypeName(2)); + assertEquals("GEOGRAPHY", rsmd.getColumnTypeName(3)); + + // Validate data + int rowCount = 0; + while (rs.next()) { + rowCount++; + int id = rs.getInt("id"); + Object geom = rs.getObject("geom"); + Object geog = rs.getObject("geog"); + + switch (id) { + case 1: + assertNotNull(geom); + assertNotNull(geog); + assertTrue(geom.toString().contains("POINT")); + assertTrue(geog.toString().contains("POINT")); + break; + case 2: + assertNotNull(geom); + assertNotNull(geog); + assertTrue(geom.toString().contains("LINESTRING")); + assertTrue(geog.toString().contains("LINESTRING")); + break; + case 3: + assertNull(geom); + assertNull(geog); + break; + default: + fail("Unexpected row id: " + id); + } + } + assertEquals(3, rowCount); + rs.close(); + } + private void closeConnection(Connection connection) throws SQLException { if (connection != null) { if (((DatabricksConnection) connection).getConnectionContext().getClientType() diff --git a/src/test/resources/cloudfetchapi/datatypesintegrationtests/testgeospatialtypes/mappings/oregon-staging_6051921418418893.jobs_sql_2025-10-10_09_results_2025-10-10t093531z_dfb38243-a074-4425-a7fe-772f480f384a-05a66b02-c06d-4941-9b01-e820827c2f85.json b/src/test/resources/cloudfetchapi/datatypesintegrationtests/testgeospatialtypes/mappings/oregon-staging_6051921418418893.jobs_sql_2025-10-10_09_results_2025-10-10t093531z_dfb38243-a074-4425-a7fe-772f480f384a-05a66b02-c06d-4941-9b01-e820827c2f85.json new file mode 100644 index 0000000000..d53c45f6da --- /dev/null +++ b/src/test/resources/cloudfetchapi/datatypesintegrationtests/testgeospatialtypes/mappings/oregon-staging_6051921418418893.jobs_sql_2025-10-10_09_results_2025-10-10t093531z_dfb38243-a074-4425-a7fe-772f480f384a-05a66b02-c06d-4941-9b01-e820827c2f85.json @@ -0,0 +1,25 @@ +{ + "id" : "05a66b02-c06d-4941-9b01-e820827c2f85", + "name" : "oregon-staging_6051921418418893.jobs_sql_2025-10-10_09_results_2025-10-10t093531z_dfb38243-a074-4425-a7fe-772f480f384a", + "request" : { + "url" : "/oregon-staging/6051921418418893.jobs/sql/2025-10-10/09/results_2025-10-10T09%3A35%3A31Z_dfb38243-a074-4425-a7fe-772f480f384a?[REDACTED]X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20251010T093531Z&X-Amz-SignedHeaders=host&X-Amz-Expires=899&[REDACTED]X-Amz-Signature=6a356f310ac3c56bbfccb7b5f5f31b34eb1010a04c8250659118a9db52f83e5f", + "method" : "GET" + }, + "response" : { + "status" : 200, + "base64Body" : "BCJNGHRwjroAAACg/////wgBAAAQAAEAsAoADgAGAA0ACAAKDwAiAAQYAKEBCgAMAAAACAAEGAARCAwAASEAoQADAAAAkAAAAEguAKQAANL///8UAAAABABCAAAFAVwAAgIAQMD///8oAPUAZ2VvZwAAEgAYABQAEwASYAAXEjwAERgzACAFAUwABAIAEQQCAAAIAAFEABRtRAA/AAATRAACEhw5ACICIAgAAQIAdAgADAAIAAfEABABHACAAgAAAGlkAABLcvQ0AAAAAGc6z14EIk0YdHCOHAEAAKL/////CAEAABQAAQDyAgwAFgAOABUAEAAEAAwAAACoFwBgAAAABAAQCADSAwoAGAAMAAgABAAKADgAUJgAAAADHAADAgATCAgAAAIAEAEFAAcUAABUAAACABAYBQAHIAARIA4AEgBkAEEAAAAwBgAwAAAhBQBBAAAAWAYABjAAEGANAAcwABFwDgAwAAAxBQADAgAAlAAABAAIAgAEEAAEmAAMEAATBy0AACAAFwLYAAQoAAACAAD4AACYAAAEAPASUE9JTlQoMSAyKUxJTkVTVFJJTkcoMCAwLDEgMSwyIDIpMQAHSAAAAgAAbAEAqAAABACiU1JJRD00MzI2O0oARjMgNCkUAAdUAPAANSA1LDYgNikAAAAAAAAA84O3awAAAACLf1BBBCJNGHRwjggAAID/////AAAAAIaGkggAAAAAhoaSCA==", + "headers" : { + "Accept-Ranges" : "bytes", + "Server" : "AmazonS3", + "ETag" : "\"c0447953da35669e9a3629504add95a8\"", + "Last-Modified" : "Fri, 10 Oct 2025 09:35:32 GMT", + "x-amz-request-id" : "66976VQD1VAX75SS", + "x-amz-server-side-encryption" : "AES256", + "x-amz-id-2" : "JCHXeBYwC7iGlinUE87DghMNEWmGe+GT3g2+gqGelAu6GSSpZRuBjBgl5zvhXnQ1qGnQAKanlRQ=", + "Date" : "Fri, 10 Oct 2025 09:35:33 GMT", + "Content-Type" : "binary/octet-stream" + } + }, + "uuid" : "05a66b02-c06d-4941-9b01-e820827c2f85", + "insertionIndex" : 1 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_sessions-26c29504-a4b8-440b-bbd1-d7b1d3576eb7.json b/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_sessions-26c29504-a4b8-440b-bbd1-d7b1d3576eb7.json new file mode 100644 index 0000000000..d2fbd84c5e --- /dev/null +++ b/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_sessions-26c29504-a4b8-440b-bbd1-d7b1d3576eb7.json @@ -0,0 +1,38 @@ +{ + "id" : "26c29504-a4b8-440b-bbd1-d7b1d3576eb7", + "name" : "api_2.0_sql_sessions", + "request" : { + "url" : "/api/2.0/sql/sessions/", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"warehouse_id\":\"dd43ee29fedd958d\",\"schema\":\"default\",\"catalog\":\"SPARK\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 200, + "body" : "{\"session_id\":\"01f0a5bc-7472-14b8-bc16-71f68ce36d0c\"}", + "headers" : { + "x-request-id" : "b03130c4-9753-4a1d-93f1-b8d4fe7d021f", + "date" : "Fri, 10 Oct 2025 09:35:27 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "vary" : "Accept-Encoding", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json", + "server-timing" : "request_id;dur=0;desc=\"b03130c4-9753-4a1d-93f1-b8d4fe7d021f\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "26c29504-a4b8-440b-bbd1-d7b1d3576eb7", + "scenarioName" : "scenario-1-api-2.0-sql-sessions", + "requiredScenarioState" : "Started", + "newScenarioState" : "scenario-1-api-2.0-sql-sessions-2", + "insertionIndex" : 7 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_sessions-53ba8b00-9e82-4ffe-a90a-aa18dfea3f89.json b/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_sessions-53ba8b00-9e82-4ffe-a90a-aa18dfea3f89.json new file mode 100644 index 0000000000..85e8fb750b --- /dev/null +++ b/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_sessions-53ba8b00-9e82-4ffe-a90a-aa18dfea3f89.json @@ -0,0 +1,37 @@ +{ + "id" : "53ba8b00-9e82-4ffe-a90a-aa18dfea3f89", + "name" : "api_2.0_sql_sessions", + "request" : { + "url" : "/api/2.0/sql/sessions/", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"warehouse_id\":\"dd43ee29fedd958d\",\"schema\":\"default\",\"catalog\":\"SPARK\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 200, + "body" : "{\"session_id\":\"01f0a5bc-75de-14b1-aba0-dd791b1a50e3\"}", + "headers" : { + "x-request-id" : "b7cfeca3-2f54-4a9b-84b4-d99dedd08fe3", + "date" : "Fri, 10 Oct 2025 09:35:29 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "vary" : "Accept-Encoding", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json", + "server-timing" : "request_id;dur=0;desc=\"b7cfeca3-2f54-4a9b-84b4-d99dedd08fe3\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "53ba8b00-9e82-4ffe-a90a-aa18dfea3f89", + "scenarioName" : "scenario-1-api-2.0-sql-sessions", + "requiredScenarioState" : "scenario-1-api-2.0-sql-sessions-2", + "insertionIndex" : 5 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_sessions_01f0a5bc-7472-14b8-bc16-71f68ce36d0c-423765cb-ebcc-4198-8b3e-2c1997822b1c.json b/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_sessions_01f0a5bc-7472-14b8-bc16-71f68ce36d0c-423765cb-ebcc-4198-8b3e-2c1997822b1c.json new file mode 100644 index 0000000000..b25b01ec9e --- /dev/null +++ b/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_sessions_01f0a5bc-7472-14b8-bc16-71f68ce36d0c-423765cb-ebcc-4198-8b3e-2c1997822b1c.json @@ -0,0 +1,29 @@ +{ + "id" : "423765cb-ebcc-4198-8b3e-2c1997822b1c", + "name" : "api_2.0_sql_sessions_01f0a5bc-7472-14b8-bc16-71f68ce36d0c", + "request" : { + "url" : "/api/2.0/sql/sessions/01f0a5bc-7472-14b8-bc16-71f68ce36d0c?warehouse_id=dd43ee29fedd958d", + "method" : "DELETE" + }, + "response" : { + "status" : 200, + "body" : "{}", + "headers" : { + "x-request-id" : "ed1c1402-4910-4360-b08a-9040377de512", + "date" : "Fri, 10 Oct 2025 09:35:35 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json", + "server-timing" : "request_id;dur=0;desc=\"ed1c1402-4910-4360-b08a-9040377de512\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "423765cb-ebcc-4198-8b3e-2c1997822b1c", + "insertionIndex" : 2 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_sessions_01f0a5bc-75de-14b1-aba0-dd791b1a50e3-838649f2-ff8b-416a-b28f-e9bef5a36f7e.json b/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_sessions_01f0a5bc-75de-14b1-aba0-dd791b1a50e3-838649f2-ff8b-416a-b28f-e9bef5a36f7e.json new file mode 100644 index 0000000000..b7006d771d --- /dev/null +++ b/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_sessions_01f0a5bc-75de-14b1-aba0-dd791b1a50e3-838649f2-ff8b-416a-b28f-e9bef5a36f7e.json @@ -0,0 +1,29 @@ +{ + "id" : "838649f2-ff8b-416a-b28f-e9bef5a36f7e", + "name" : "api_2.0_sql_sessions_01f0a5bc-75de-14b1-aba0-dd791b1a50e3", + "request" : { + "url" : "/api/2.0/sql/sessions/01f0a5bc-75de-14b1-aba0-dd791b1a50e3?warehouse_id=dd43ee29fedd958d", + "method" : "DELETE" + }, + "response" : { + "status" : 200, + "body" : "{}", + "headers" : { + "x-request-id" : "020075a5-a0e8-4c1f-8885-e395d89fc45e", + "date" : "Fri, 10 Oct 2025 09:35:36 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json", + "server-timing" : "request_id;dur=0;desc=\"020075a5-a0e8-4c1f-8885-e395d89fc45e\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "838649f2-ff8b-416a-b28f-e9bef5a36f7e", + "insertionIndex" : 1 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_statements-1bc97f2a-5ba2-48a6-a701-819fd4d1fd53.json b/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_statements-1bc97f2a-5ba2-48a6-a701-819fd4d1fd53.json new file mode 100644 index 0000000000..ca8e90737f --- /dev/null +++ b/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_statements-1bc97f2a-5ba2-48a6-a701-819fd4d1fd53.json @@ -0,0 +1,35 @@ +{ + "id" : "1bc97f2a-5ba2-48a6-a701-819fd4d1fd53", + "name" : "api_2.0_sql_statements", + "request" : { + "url" : "/api/2.0/sql/statements/", + "method" : "POST", + "bodyPatterns" : [ { + "equalToJson" : "{\"statement\":\"SELECT * FROM (VALUES (1, ST_GeomFromText('POINT (1 2)'), ST_GeogFromText('POINT (3 4)')), (2, ST_GeomFromText('LINESTRING (0 0, 1 1, 2 2)'), ST_GeogFromText('LINESTRING (5 5, 6 6)')), (3, NULL, NULL)) AS geospatial_data(id, geom, geog) ORDER BY id\",\"warehouse_id\":\"dd43ee29fedd958d\",\"session_id\":\"01f0a5bc-7472-14b8-bc16-71f68ce36d0c\",\"disposition\":\"EXTERNAL_LINKS\",\"format\":\"ARROW_STREAM\",\"on_wait_timeout\":\"CONTINUE\",\"parameters\":[],\"result_compression\":\"LZ4_FRAME\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : true + } ] + }, + "response" : { + "status" : 200, + "body" : "{\"statement_id\":\"01f0a5bc-7691-1333-a867-e256984f11cf\",\"status\":{\"state\":\"SUCCEEDED\"},\"manifest\":{\"format\":\"ARROW_STREAM\",\"schema\":{\"column_count\":3,\"columns\":[{\"name\":\"id\",\"type_text\":\"INT\",\"type_name\":\"INT\",\"position\":0},{\"name\":\"geom\",\"type_text\":\"GEOMETRY(0)\",\"type_name\":\"GEOMETRY\",\"position\":1},{\"name\":\"geog\",\"type_text\":\"GEOGRAPHY(4326)\",\"type_name\":\"GEOGRAPHY\",\"position\":2}]},\"total_chunk_count\":1,\"chunks\":[{\"chunk_index\":0,\"row_offset\":0,\"row_count\":3,\"byte_count\":720}],\"total_row_count\":3,\"total_byte_count\":720,\"truncated\":false,\"result_compression\":\"LZ4_FRAME\"},\"result\":{\"external_links\":[{\"chunk_index\":0,\"row_offset\":0,\"row_count\":3,\"byte_count\":547,\"external_link\":\"https://e2-dogfood-core.s3.us-west-2.amazonaws.com/oregon-staging/6051921418418893.jobs/sql/2025-10-10/09/results_2025-10-10T09%3A35%3A31Z_dfb38243-a074-4425-a7fe-772f480f384a?[REDACTED]X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20251010T093531Z&X-Amz-SignedHeaders=host&X-Amz-Expires=899&[REDACTED]X-Amz-Signature=6a356f310ac3c56bbfccb7b5f5f31b34eb1010a04c8250659118a9db52f83e5f\",\"expiration\":\"2025-10-10T09:50:31.512Z\"}]}}", + "headers" : { + "x-request-id" : "c41ca866-9974-4e14-83c2-a6c072d9a33c", + "date" : "Fri, 10 Oct 2025 09:35:31 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "vary" : "Accept-Encoding", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json", + "server-timing" : "request_id;dur=0;desc=\"c41ca866-9974-4e14-83c2-a6c072d9a33c\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "1bc97f2a-5ba2-48a6-a701-819fd4d1fd53", + "insertionIndex" : 4 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_statements_01f0a5bc-7691-1333-a867-e256984f11cf-03a6959b-2f46-41af-9cc0-dfd458a4d0ac.json b/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_statements_01f0a5bc-7691-1333-a867-e256984f11cf-03a6959b-2f46-41af-9cc0-dfd458a4d0ac.json new file mode 100644 index 0000000000..a51a78f2c7 --- /dev/null +++ b/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/api_2.0_sql_statements_01f0a5bc-7691-1333-a867-e256984f11cf-03a6959b-2f46-41af-9cc0-dfd458a4d0ac.json @@ -0,0 +1,29 @@ +{ + "id" : "03a6959b-2f46-41af-9cc0-dfd458a4d0ac", + "name" : "api_2.0_sql_statements_01f0a5bc-7691-1333-a867-e256984f11cf", + "request" : { + "url" : "/api/2.0/sql/statements/01f0a5bc-7691-1333-a867-e256984f11cf", + "method" : "DELETE" + }, + "response" : { + "status" : 200, + "body" : "{}", + "headers" : { + "x-request-id" : "b59b889c-e6b6-4c88-af81-ace62198e27f", + "date" : "Fri, 10 Oct 2025 09:35:34 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json", + "server-timing" : "request_id;dur=0;desc=\"b59b889c-e6b6-4c88-af81-ace62198e27f\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "03a6959b-2f46-41af-9cc0-dfd458a4d0ac", + "insertionIndex" : 3 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/oidc_.well-known_oauth-authorization-server-75d1e2b1-4917-4fc3-bc23-63e9201f71c2.json b/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/oidc_.well-known_oauth-authorization-server-75d1e2b1-4917-4fc3-bc23-63e9201f71c2.json new file mode 100644 index 0000000000..1d8f93b433 --- /dev/null +++ b/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/oidc_.well-known_oauth-authorization-server-75d1e2b1-4917-4fc3-bc23-63e9201f71c2.json @@ -0,0 +1,36 @@ +{ + "id" : "75d1e2b1-4917-4fc3-bc23-63e9201f71c2", + "name" : "oidc_.well-known_oauth-authorization-server", + "request" : { + "url" : "/oidc/.well-known/oauth-authorization-server", + "method" : "GET" + }, + "response" : { + "status" : 200, + "body" : "{\"authorization_endpoint\":\"https:\\/\\/e2-dogfood.staging.cloud.databricks.com\\/oidc\\/v1\\/authorize\",\"token_endpoint\":\"https:\\/\\/e2-dogfood.staging.cloud.databricks.com\\/oidc\\/v1\\/token\",\"issuer\":\"https:\\/\\/e2-dogfood.staging.cloud.databricks.com\\/oidc\",\"jwks_uri\":\"https:\\/\\/oregon.staging.cloud.databricks.com\\/oidc\\/jwks.json\",\"scopes_supported\":[\"all-apis\",\"apps.apps\",\"catalog.artifact-allowlists\",\"catalog.catalogs\",\"catalog.connections\",\"catalog.credentials\",\"catalog.entity-tag-assignments\",\"catalog.external-lineage\",\"catalog.external-locations\",\"catalog.external-metadata\",\"catalog.functions\",\"catalog.grants\",\"catalog.metastores\",\"catalog.model-versions\",\"catalog.online-tables\",\"catalog.policies\",\"catalog.quality-monitors\",\"catalog.registered-models\",\"catalog.resource-quotas\",\"catalog.rfa\",\"catalog.schemas\",\"catalog.storage-credentials\",\"catalog.system-schemas\",\"catalog.table-constraints\",\"catalog.tables\",\"catalog.temporary-path-credentials\",\"catalog.temporary-table-credentials\",\"catalog.volumes\",\"catalog.workspace-bindings\",\"cleanrooms.clean-room-asset-revisions\",\"cleanrooms.clean-room-assets\",\"cleanrooms.clean-room-auto-approval-rules\",\"cleanrooms.clean-room-task-runs\",\"cleanrooms.clean-rooms\",\"compute.cluster-policies\",\"compute.clusters\",\"compute.command-execution\",\"compute.global-init-scripts\",\"compute.instance-pools\",\"compute.instance-profiles\",\"compute.libraries\",\"compute.policy-compliance-for-clusters\",\"compute.policy-families\",\"dashboards.genie\",\"dashboards.lakeview\",\"dashboards.lakeview-embedded\",\"database.database\",\"email\",\"files.dbfs\",\"files.files\",\"iam.account-access-control-proxy\",\"iam.current-user\",\"iam.groups\",\"iam.permissions\",\"iam.service-principals\",\"iam.users\",\"iamv2.iam\",\"jobs.jobs\",\"jobs.policy-compliance-for-jobs\",\"marketplace.consumer-fulfillments\",\"marketplace.consumer-installations\",\"marketplace.consumer-listings\",\"marketplace.consumer-personalization-requests\",\"marketplace.consumer-providers\",\"marketplace.provider-exchange-filters\",\"marketplace.provider-exchanges\",\"marketplace.provider-files\",\"marketplace.provider-listings\",\"marketplace.provider-personalization-requests\",\"marketplace.provider-provider-analytics-dashboards\",\"marketplace.provider-providers\",\"mcp.external\",\"mcp.functions\",\"mcp.genie\",\"mcp.sql\",\"mcp.vectorsearch\",\"ml.experiments\",\"ml.model-registry\",\"oauth2.service-principal-secrets-proxy\",\"offline_access\",\"openid\",\"pipelines.pipelines\",\"profile\",\"qualitymonitorv2.quality-monitor\",\"serving.serving-endpoints\",\"settings.aibi-dashboard-embedding-access-policy\",\"settings.aibi-dashboard-embedding-approved-domains\",\"settings.automatic-cluster-update\",\"settings.compliance-security-profile\",\"settings.dashboard-email-subscriptions\",\"settings.default-namespace\",\"settings.disable-legacy-access\",\"settings.disable-legacy-dbfs\",\"settings.enable-export-notebook\",\"settings.enable-notebook-table-clipboard\",\"settings.enable-results-downloading\",\"settings.enhanced-security-monitoring\",\"settings.ip-access-lists\",\"settings.notification-destinations\",\"settings.restrict-workspace-admins\",\"settings.sql-results-download\",\"settings.token-management\",\"settings.tokens\",\"settings.workspace-conf\",\"settingsv2.settings\",\"sharing.providers\",\"sharing.recipient-activation\",\"sharing.recipients\",\"sharing.shares\",\"sql\",\"sql.alerts\",\"sql.alerts-legacy\",\"sql.alerts-v2\",\"sql.dashboards\",\"sql.data-sources\",\"sql.dbsql-permissions\",\"sql.driver\",\"sql.queries\",\"sql.queries-legacy\",\"sql.query-history\",\"sql.statement-execution\",\"sql.warehouses\",\"tags.tag-policies\",\"vectorsearch.vector-search-endpoints\",\"vectorsearch.vector-search-indexes\",\"workspace.git-credentials\",\"workspace.repos\",\"workspace.secrets\",\"workspace.workspace\"],\"response_types_supported\":[\"code\",\"id_token\"],\"response_modes_supported\":[\"query\",\"fragment\",\"form_post\"],\"grant_types_supported\":[\"client_credentials\",\"authorization_code\",\"refresh_token\"],\"code_challenge_methods_supported\":[\"S256\"],\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"none\"],\"subject_types_supported\":[\"public\"],\"id_token_signing_alg_values_supported\":[\"RS256\"],\"claims_supported\":[\"iss\",\"sub\",\"aud\",\"iat\",\"exp\",\"jti\",\"name\",\"family_name\",\"given_name\",\"preferred_username\"],\"request_uri_parameter_supported\":false}", + "headers" : { + "x-request-id" : "8b5b037b-ab96-4532-9ee6-a983e18118fd", + "date" : "Fri, 10 Oct 2025 09:35:26 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "vary" : "Accept-Encoding", + "access-control-allow-headers" : "Content-Type", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "access-control-allow-methods" : "GET", + "access-control-allow-origin" : "*", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json; charset=UTF-8", + "server-timing" : "request_id;dur=0;desc=\"8b5b037b-ab96-4532-9ee6-a983e18118fd\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "75d1e2b1-4917-4fc3-bc23-63e9201f71c2", + "scenarioName" : "scenario-2-oidc-.well-known-oauth-authorization-server", + "requiredScenarioState" : "Started", + "newScenarioState" : "scenario-2-oidc-.well-known-oauth-authorization-server-2", + "insertionIndex" : 8 +} \ No newline at end of file diff --git a/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/oidc_.well-known_oauth-authorization-server-872f82e0-1642-4fc5-a033-23dc902aed10.json b/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/oidc_.well-known_oauth-authorization-server-872f82e0-1642-4fc5-a033-23dc902aed10.json new file mode 100644 index 0000000000..16a3edb92c --- /dev/null +++ b/src/test/resources/sqlexecapi/datatypesintegrationtests/testgeospatialtypes/mappings/oidc_.well-known_oauth-authorization-server-872f82e0-1642-4fc5-a033-23dc902aed10.json @@ -0,0 +1,35 @@ +{ + "id" : "872f82e0-1642-4fc5-a033-23dc902aed10", + "name" : "oidc_.well-known_oauth-authorization-server", + "request" : { + "url" : "/oidc/.well-known/oauth-authorization-server", + "method" : "GET" + }, + "response" : { + "status" : 200, + "body" : "{\"authorization_endpoint\":\"https:\\/\\/e2-dogfood.staging.cloud.databricks.com\\/oidc\\/v1\\/authorize\",\"token_endpoint\":\"https:\\/\\/e2-dogfood.staging.cloud.databricks.com\\/oidc\\/v1\\/token\",\"issuer\":\"https:\\/\\/e2-dogfood.staging.cloud.databricks.com\\/oidc\",\"jwks_uri\":\"https:\\/\\/oregon.staging.cloud.databricks.com\\/oidc\\/jwks.json\",\"scopes_supported\":[\"all-apis\",\"apps.apps\",\"catalog.artifact-allowlists\",\"catalog.catalogs\",\"catalog.connections\",\"catalog.credentials\",\"catalog.entity-tag-assignments\",\"catalog.external-lineage\",\"catalog.external-locations\",\"catalog.external-metadata\",\"catalog.functions\",\"catalog.grants\",\"catalog.metastores\",\"catalog.model-versions\",\"catalog.online-tables\",\"catalog.policies\",\"catalog.quality-monitors\",\"catalog.registered-models\",\"catalog.resource-quotas\",\"catalog.rfa\",\"catalog.schemas\",\"catalog.storage-credentials\",\"catalog.system-schemas\",\"catalog.table-constraints\",\"catalog.tables\",\"catalog.temporary-path-credentials\",\"catalog.temporary-table-credentials\",\"catalog.volumes\",\"catalog.workspace-bindings\",\"cleanrooms.clean-room-asset-revisions\",\"cleanrooms.clean-room-assets\",\"cleanrooms.clean-room-auto-approval-rules\",\"cleanrooms.clean-room-task-runs\",\"cleanrooms.clean-rooms\",\"compute.cluster-policies\",\"compute.clusters\",\"compute.command-execution\",\"compute.global-init-scripts\",\"compute.instance-pools\",\"compute.instance-profiles\",\"compute.libraries\",\"compute.policy-compliance-for-clusters\",\"compute.policy-families\",\"dashboards.genie\",\"dashboards.lakeview\",\"dashboards.lakeview-embedded\",\"database.database\",\"email\",\"files.dbfs\",\"files.files\",\"iam.account-access-control-proxy\",\"iam.current-user\",\"iam.groups\",\"iam.permissions\",\"iam.service-principals\",\"iam.users\",\"iamv2.iam\",\"jobs.jobs\",\"jobs.policy-compliance-for-jobs\",\"marketplace.consumer-fulfillments\",\"marketplace.consumer-installations\",\"marketplace.consumer-listings\",\"marketplace.consumer-personalization-requests\",\"marketplace.consumer-providers\",\"marketplace.provider-exchange-filters\",\"marketplace.provider-exchanges\",\"marketplace.provider-files\",\"marketplace.provider-listings\",\"marketplace.provider-personalization-requests\",\"marketplace.provider-provider-analytics-dashboards\",\"marketplace.provider-providers\",\"mcp.external\",\"mcp.functions\",\"mcp.genie\",\"mcp.sql\",\"mcp.vectorsearch\",\"ml.experiments\",\"ml.model-registry\",\"oauth2.service-principal-secrets-proxy\",\"offline_access\",\"openid\",\"pipelines.pipelines\",\"profile\",\"qualitymonitorv2.quality-monitor\",\"serving.serving-endpoints\",\"settings.aibi-dashboard-embedding-access-policy\",\"settings.aibi-dashboard-embedding-approved-domains\",\"settings.automatic-cluster-update\",\"settings.compliance-security-profile\",\"settings.dashboard-email-subscriptions\",\"settings.default-namespace\",\"settings.disable-legacy-access\",\"settings.disable-legacy-dbfs\",\"settings.enable-export-notebook\",\"settings.enable-notebook-table-clipboard\",\"settings.enable-results-downloading\",\"settings.enhanced-security-monitoring\",\"settings.ip-access-lists\",\"settings.notification-destinations\",\"settings.restrict-workspace-admins\",\"settings.sql-results-download\",\"settings.token-management\",\"settings.tokens\",\"settings.workspace-conf\",\"settingsv2.settings\",\"sharing.providers\",\"sharing.recipient-activation\",\"sharing.recipients\",\"sharing.shares\",\"sql\",\"sql.alerts\",\"sql.alerts-legacy\",\"sql.alerts-v2\",\"sql.dashboards\",\"sql.data-sources\",\"sql.dbsql-permissions\",\"sql.driver\",\"sql.queries\",\"sql.queries-legacy\",\"sql.query-history\",\"sql.statement-execution\",\"sql.warehouses\",\"tags.tag-policies\",\"vectorsearch.vector-search-endpoints\",\"vectorsearch.vector-search-indexes\",\"workspace.git-credentials\",\"workspace.repos\",\"workspace.secrets\",\"workspace.workspace\"],\"response_types_supported\":[\"code\",\"id_token\"],\"response_modes_supported\":[\"query\",\"fragment\",\"form_post\"],\"grant_types_supported\":[\"client_credentials\",\"authorization_code\",\"refresh_token\"],\"code_challenge_methods_supported\":[\"S256\"],\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"none\"],\"subject_types_supported\":[\"public\"],\"id_token_signing_alg_values_supported\":[\"RS256\"],\"claims_supported\":[\"iss\",\"sub\",\"aud\",\"iat\",\"exp\",\"jti\",\"name\",\"family_name\",\"given_name\",\"preferred_username\"],\"request_uri_parameter_supported\":false}", + "headers" : { + "x-request-id" : "dfe1201f-10ce-430a-81ba-0404d9248468", + "date" : "Fri, 10 Oct 2025 09:35:28 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "vary" : "Accept-Encoding", + "access-control-allow-headers" : "Content-Type", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "access-control-allow-methods" : "GET", + "access-control-allow-origin" : "*", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json; charset=UTF-8", + "server-timing" : "request_id;dur=0;desc=\"dfe1201f-10ce-430a-81ba-0404d9248468\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "872f82e0-1642-4fc5-a033-23dc902aed10", + "scenarioName" : "scenario-2-oidc-.well-known-oauth-authorization-server", + "requiredScenarioState" : "scenario-2-oidc-.well-known-oauth-authorization-server-2", + "insertionIndex" : 6 +} \ No newline at end of file From b01f240670b8f9725458df92b886acc98dfaf69a Mon Sep 17 00:00:00 2001 From: Sreekanth Vadigi Date: Fri, 10 Oct 2025 16:23:38 +0530 Subject: [PATCH 08/14] addressing review comments - 3 Signed-off-by: Sreekanth Vadigi --- .../converters/ArrowToJavaObjectConverter.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverter.java b/src/main/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverter.java index 634977c803..cb81f6b6c8 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverter.java +++ b/src/main/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverter.java @@ -10,6 +10,7 @@ import com.databricks.jdbc.log.JdbcLoggerFactory; import com.databricks.jdbc.model.core.ColumnInfo; import com.databricks.jdbc.model.core.ColumnInfoTypeName; +import com.databricks.jdbc.model.telemetry.enums.DatabricksDriverErrorCode; import java.math.BigDecimal; import java.math.RoundingMode; import java.sql.Date; @@ -176,8 +177,7 @@ private static Object convertToStruct(Object object, String arrowMetadata) } private static AbstractDatabricksGeospatial convertToGeospatial( - Object object, String arrowMetadata, ColumnInfoTypeName type) - throws DatabricksValidationException { + Object object, String arrowMetadata, ColumnInfoTypeName type) throws DatabricksSQLException { String ewkt = convertToString(object); // Parse EWKT to extract SRID from data if present @@ -334,8 +334,10 @@ private static T convertToNumber( * @param metadata Arrow metadata like "GEOMETRY(32633)" or "GEOGRAPHY(4326)" * @param typePrefix The prefix to look for ("GEOMETRY" or "GEOGRAPHY") * @return SRID value, or 0 if not found + * @throws DatabricksParsingException if metadata format is invalid */ - private static int extractSRIDFromMetadata(String metadata, String typePrefix) { + private static int extractSRIDFromMetadata(String metadata, String typePrefix) + throws DatabricksParsingException { if (metadata == null) { return 0; } @@ -350,7 +352,11 @@ private static int extractSRIDFromMetadata(String metadata, String typePrefix) { return Integer.parseInt(m.group(1)); } } catch (Exception e) { - LOGGER.debug("Failed to extract SRID from metadata: {}", metadata, e); + String errorMessage = + String.format("Failed to parse SRID from %s metadata: %s", typePrefix, metadata); + LOGGER.error(errorMessage, e); + throw new DatabricksParsingException( + errorMessage, e, DatabricksDriverErrorCode.RESULT_SET_ERROR); } return 0; From 61bfff4e1c6e711530a33179ff1edf69793d423f Mon Sep 17 00:00:00 2001 From: Sreekanth Vadigi Date: Mon, 13 Oct 2025 17:25:00 +0530 Subject: [PATCH 09/14] addressing review comments - 4 Signed-off-by: Sreekanth Vadigi --- .../jdbc/api/impl/AbstractDatabricksGeospatial.java | 7 +------ ...atabricksGeospatial.java => IDatabricksGeospatial.java} | 2 +- .../jdbc/api/impl/converters/GeospatialConverter.java | 6 +++--- 3 files changed, 5 insertions(+), 10 deletions(-) rename src/main/java/com/databricks/jdbc/api/impl/{DatabricksGeospatial.java => IDatabricksGeospatial.java} (97%) diff --git a/src/main/java/com/databricks/jdbc/api/impl/AbstractDatabricksGeospatial.java b/src/main/java/com/databricks/jdbc/api/impl/AbstractDatabricksGeospatial.java index 6fec75a9ea..3754eb5ece 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/AbstractDatabricksGeospatial.java +++ b/src/main/java/com/databricks/jdbc/api/impl/AbstractDatabricksGeospatial.java @@ -2,8 +2,6 @@ import com.databricks.jdbc.api.impl.converters.WKTConverter; import com.databricks.jdbc.exception.DatabricksValidationException; -import com.databricks.jdbc.log.JdbcLogger; -import com.databricks.jdbc.log.JdbcLoggerFactory; import java.util.Objects; /** @@ -12,10 +10,7 @@ *

This class provides common functionality for both GEOMETRY and GEOGRAPHY types, including * storage of WKT (Well-Known Text) format data and access to both WKT and WKB representations. */ -public abstract class AbstractDatabricksGeospatial implements DatabricksGeospatial { - - private static final JdbcLogger LOGGER = - JdbcLoggerFactory.getLogger(AbstractDatabricksGeospatial.class); +public abstract class AbstractDatabricksGeospatial implements IDatabricksGeospatial { private final String wkt; private final int srid; diff --git a/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeospatial.java b/src/main/java/com/databricks/jdbc/api/impl/IDatabricksGeospatial.java similarity index 97% rename from src/main/java/com/databricks/jdbc/api/impl/DatabricksGeospatial.java rename to src/main/java/com/databricks/jdbc/api/impl/IDatabricksGeospatial.java index ad6cdace23..34a2515b10 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeospatial.java +++ b/src/main/java/com/databricks/jdbc/api/impl/IDatabricksGeospatial.java @@ -12,7 +12,7 @@ *

Following the established patterns of DatabricksStruct, DatabricksArray, and DatabricksMap, * this interface enables consistent handling of geospatial data across the JDBC driver. */ -public interface DatabricksGeospatial { +public interface IDatabricksGeospatial { /** * Returns the Well-Known Binary (WKB) representation of the geospatial object. diff --git a/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java b/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java index 82231434f3..9d60cab9c6 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java +++ b/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java @@ -2,7 +2,7 @@ import com.databricks.jdbc.api.impl.DatabricksGeography; import com.databricks.jdbc.api.impl.DatabricksGeometry; -import com.databricks.jdbc.api.impl.DatabricksGeospatial; +import com.databricks.jdbc.api.impl.IDatabricksGeospatial; import com.databricks.jdbc.exception.DatabricksSQLException; import com.databricks.jdbc.log.JdbcLogger; import com.databricks.jdbc.log.JdbcLoggerFactory; @@ -72,8 +72,8 @@ public String toString(Object object) throws DatabricksSQLException { @Override public byte[] toByteArray(Object object) throws DatabricksSQLException { - if (object instanceof DatabricksGeospatial) { - return ((DatabricksGeospatial) object).getWkb(); + if (object instanceof IDatabricksGeospatial) { + return ((IDatabricksGeospatial) object).getWkb(); } throw new DatabricksSQLException( "Unsupported byte array conversion operation for geospatial types", From f080ff65482a88108e7db953f68db34d32912874 Mon Sep 17 00:00:00 2001 From: Sreekanth Vadigi Date: Wed, 15 Oct 2025 12:39:57 +0530 Subject: [PATCH 10/14] addressing review comments - 5 Signed-off-by: Sreekanth Vadigi --- NOTICE | 6 +++++ .../impl/AbstractDatabricksGeospatial.java | 22 +++++++++++++++---- .../jdbc/api/impl/DatabricksGeography.java | 7 ++++++ .../jdbc/api/impl/DatabricksGeometry.java | 7 ++++++ .../jdbc/api/impl/IDatabricksGeospatial.java | 13 ++++++++--- .../ArrowToJavaObjectConverter.java | 5 +++++ .../impl/converters/GeospatialConverter.java | 2 +- .../api/impl/DatabricksGeographyTest.java | 20 ++++++++--------- .../jdbc/api/impl/DatabricksGeometryTest.java | 22 +++++++++---------- .../ArrowToJavaObjectConverterTest.java | 8 +++---- .../converters/GeospatialConverterTest.java | 18 +++++++-------- 11 files changed, 88 insertions(+), 42 deletions(-) diff --git a/NOTICE b/NOTICE index 2025efe26c..6fd09f3c5a 100644 --- a/NOTICE +++ b/NOTICE @@ -80,6 +80,12 @@ jakartaee/common-annotations-api - https://github.com/jakartaee/common-annotatio Copyright common-annotations-api authors Notice - https://github.com/jakartaee/common-annotations-api/blob/master/NOTICE.md +locationtech/jts - https://github.com/locationtech/jts +Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors +Copyright LocationTech JTS contributors +License: Eclipse Public License v2.0 (EPL-2.0) and Eclipse Distribution License v1.0 (EDL-1.0) +License URLs: https://github.com/locationtech/jts/blob/master/LICENSE_EPLv2.txt, https://github.com/locationtech/jts/blob/master/LICENSE_EDLv1.txt + ________________ This Software contains code from the following open source projects, licensed under the GNU Lesser General Public License (https://www.gnu.org/licenses/lgpl-3.0.html): diff --git a/src/main/java/com/databricks/jdbc/api/impl/AbstractDatabricksGeospatial.java b/src/main/java/com/databricks/jdbc/api/impl/AbstractDatabricksGeospatial.java index 3754eb5ece..a344973e38 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/AbstractDatabricksGeospatial.java +++ b/src/main/java/com/databricks/jdbc/api/impl/AbstractDatabricksGeospatial.java @@ -2,6 +2,8 @@ import com.databricks.jdbc.api.impl.converters.WKTConverter; import com.databricks.jdbc.exception.DatabricksValidationException; +import com.databricks.jdbc.log.JdbcLogger; +import com.databricks.jdbc.log.JdbcLoggerFactory; import java.util.Objects; /** @@ -12,8 +14,11 @@ */ public abstract class AbstractDatabricksGeospatial implements IDatabricksGeospatial { + private static final JdbcLogger LOGGER = + JdbcLoggerFactory.getLogger(AbstractDatabricksGeospatial.class); + private final String wkt; - private final int srid; + private final int srid; // Spatial Reference System Identifier /** * Constructs an AbstractDatabricksGeospatial with the specified WKT and SRID. @@ -25,6 +30,7 @@ public abstract class AbstractDatabricksGeospatial implements IDatabricksGeospat protected AbstractDatabricksGeospatial(String wkt, int srid) throws DatabricksValidationException { if (wkt == null || wkt.trim().isEmpty()) { + LOGGER.error("WKT string cannot be null or empty"); throw new DatabricksValidationException("WKT string cannot be null or empty"); } @@ -39,7 +45,7 @@ protected AbstractDatabricksGeospatial(String wkt, int srid) * @throws DatabricksValidationException if WKT to WKB conversion fails */ @Override - public byte[] getWkb() throws DatabricksValidationException { + public byte[] getWKB() throws DatabricksValidationException { return WKTConverter.toWKB(wkt); } @@ -49,7 +55,7 @@ public byte[] getWkb() throws DatabricksValidationException { * @return the SRID value */ @Override - public int getSrid() { + public int getSRID() { return srid; } @@ -59,7 +65,7 @@ public int getSrid() { * @return the WKT string */ @Override - public String getWkt() { + public String getWKT() { return wkt; } @@ -101,4 +107,12 @@ public boolean equals(Object obj) { public int hashCode() { return Objects.hash(wkt, srid); } + + /** + * Returns the data type of the geospatial object. + * + * @return the type as a string, either "GEOMETRY" or "GEOGRAPHY" + */ + @Override + public abstract String getType(); } diff --git a/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeography.java b/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeography.java index a1532af840..2681dac401 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeography.java +++ b/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeography.java @@ -1,5 +1,7 @@ package com.databricks.jdbc.api.impl; +import static com.databricks.jdbc.common.util.DatabricksTypeUtil.GEOGRAPHY; + import com.databricks.jdbc.exception.DatabricksValidationException; public class DatabricksGeography extends AbstractDatabricksGeospatial { @@ -14,4 +16,9 @@ public class DatabricksGeography extends AbstractDatabricksGeospatial { public DatabricksGeography(String wkt, int srid) throws DatabricksValidationException { super(wkt, srid); } + + @Override + public String getType() { + return GEOGRAPHY; + } } diff --git a/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeometry.java b/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeometry.java index a5a019aba4..e41535f560 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeometry.java +++ b/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeometry.java @@ -1,5 +1,7 @@ package com.databricks.jdbc.api.impl; +import static com.databricks.jdbc.common.util.DatabricksTypeUtil.GEOMETRY; + import com.databricks.jdbc.exception.DatabricksValidationException; public class DatabricksGeometry extends AbstractDatabricksGeospatial { @@ -14,4 +16,9 @@ public class DatabricksGeometry extends AbstractDatabricksGeospatial { public DatabricksGeometry(String wkt, int srid) throws DatabricksValidationException { super(wkt, srid); } + + @Override + public String getType() { + return GEOMETRY; + } } diff --git a/src/main/java/com/databricks/jdbc/api/impl/IDatabricksGeospatial.java b/src/main/java/com/databricks/jdbc/api/impl/IDatabricksGeospatial.java index 34a2515b10..2197343370 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/IDatabricksGeospatial.java +++ b/src/main/java/com/databricks/jdbc/api/impl/IDatabricksGeospatial.java @@ -24,7 +24,7 @@ public interface IDatabricksGeospatial { * @return the WKB representation as a byte array * @throws DatabricksValidationException if WKT to WKB conversion fails */ - byte[] getWkb() throws DatabricksValidationException; + byte[] getWKB() throws DatabricksValidationException; /** * Returns the Spatial Reference System Identifier (SRID) of the geospatial object. @@ -39,7 +39,7 @@ public interface IDatabricksGeospatial { * * @return the SRID value */ - int getSrid(); + int getSRID(); /** * Returns the Well-Known Text (WKT) representation of the geospatial object. @@ -49,5 +49,12 @@ public interface IDatabricksGeospatial { * * @return the WKT string representation */ - String getWkt(); + String getWKT(); + + /** + * Returns the data type of the geospatial object. + * + * @return the type as a string, either "GEOMETRY" or "GEOGRAPHY" + */ + String getType(); } diff --git a/src/main/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverter.java b/src/main/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverter.java index cb81f6b6c8..d39a4511af 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverter.java +++ b/src/main/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverter.java @@ -339,6 +339,7 @@ private static T convertToNumber( private static int extractSRIDFromMetadata(String metadata, String typePrefix) throws DatabricksParsingException { if (metadata == null) { + LOGGER.debug("Metadata is null, returning default SRID 0 for {}", typePrefix); return 0; } @@ -359,6 +360,10 @@ private static int extractSRIDFromMetadata(String metadata, String typePrefix) errorMessage, e, DatabricksDriverErrorCode.RESULT_SET_ERROR); } + LOGGER.debug( + "No SRID found in metadata for {}, returning default SRID 0. Metadata: {}", + typePrefix, + metadata); return 0; } } diff --git a/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java b/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java index 9d60cab9c6..48259e85da 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java +++ b/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java @@ -73,7 +73,7 @@ public String toString(Object object) throws DatabricksSQLException { @Override public byte[] toByteArray(Object object) throws DatabricksSQLException { if (object instanceof IDatabricksGeospatial) { - return ((IDatabricksGeospatial) object).getWkb(); + return ((IDatabricksGeospatial) object).getWKB(); } throw new DatabricksSQLException( "Unsupported byte array conversion operation for geospatial types", diff --git a/src/test/java/com/databricks/jdbc/api/impl/DatabricksGeographyTest.java b/src/test/java/com/databricks/jdbc/api/impl/DatabricksGeographyTest.java index b1073c02d9..fc62f2b052 100644 --- a/src/test/java/com/databricks/jdbc/api/impl/DatabricksGeographyTest.java +++ b/src/test/java/com/databricks/jdbc/api/impl/DatabricksGeographyTest.java @@ -16,16 +16,16 @@ public class DatabricksGeographyTest { public void testConstructor_WithValidPoint() throws DatabricksValidationException { DatabricksGeography geography = new DatabricksGeography("POINT(-122.4194 37.7749)", 0); assertNotNull(geography); - assertEquals("POINT(-122.4194 37.7749)", geography.getWkt()); - assertEquals(0, geography.getSrid()); + assertEquals("POINT(-122.4194 37.7749)", geography.getWKT()); + assertEquals(0, geography.getSRID()); } @Test public void testConstructor_WithValidPointAndSRID() throws DatabricksValidationException { DatabricksGeography geography = new DatabricksGeography("POINT(-122.4194 37.7749)", 4326); assertNotNull(geography); - assertEquals("POINT(-122.4194 37.7749)", geography.getWkt()); - assertEquals(4326, geography.getSrid()); + assertEquals("POINT(-122.4194 37.7749)", geography.getWKT()); + assertEquals(4326, geography.getSRID()); } @Test @@ -33,7 +33,7 @@ public void testConstructor_WithLineString() throws DatabricksValidationExceptio DatabricksGeography geography = new DatabricksGeography("LINESTRING(-122.4 37.7, -122.5 37.8, -122.6 37.9)", 4326); assertNotNull(geography); - assertTrue(geography.getWkt().startsWith("LINESTRING")); + assertTrue(geography.getWKT().startsWith("LINESTRING")); } @Test @@ -42,7 +42,7 @@ public void testConstructor_WithPolygon() throws DatabricksValidationException { new DatabricksGeography( "POLYGON((-122.4 37.7, -122.5 37.7, -122.5 37.8, -122.4 37.8, -122.4 37.7))", 4326); assertNotNull(geography); - assertTrue(geography.getWkt().startsWith("POLYGON")); + assertTrue(geography.getWKT().startsWith("POLYGON")); } @Test @@ -67,25 +67,25 @@ public void testConstructor_WithWhitespaceWKT_ThrowsException() { @Test public void testGetWkt() throws DatabricksValidationException { DatabricksGeography geography = new DatabricksGeography("POINT(-122.4194 37.7749)", 4326); - assertEquals("POINT(-122.4194 37.7749)", geography.getWkt()); + assertEquals("POINT(-122.4194 37.7749)", geography.getWKT()); } @Test public void testGetSrid_WithZero() throws DatabricksValidationException { DatabricksGeography geography = new DatabricksGeography("POINT(-122.4194 37.7749)", 0); - assertEquals(0, geography.getSrid()); + assertEquals(0, geography.getSRID()); } @Test public void testGetSrid_With3857() throws DatabricksValidationException { DatabricksGeography geography = new DatabricksGeography("POINT(-122.4194 37.7749)", 3857); - assertEquals(3857, geography.getSrid()); + assertEquals(3857, geography.getSRID()); } @Test public void testGetWkb_ReturnsValidBytes() throws DatabricksValidationException { DatabricksGeography geography = new DatabricksGeography("POINT(-122.4194 37.7749)", 4326); - byte[] wkb = geography.getWkb(); + byte[] wkb = geography.getWKB(); assertNotNull(wkb); assertTrue(wkb.length > 0); } diff --git a/src/test/java/com/databricks/jdbc/api/impl/DatabricksGeometryTest.java b/src/test/java/com/databricks/jdbc/api/impl/DatabricksGeometryTest.java index 4c148df219..fedfbbd877 100644 --- a/src/test/java/com/databricks/jdbc/api/impl/DatabricksGeometryTest.java +++ b/src/test/java/com/databricks/jdbc/api/impl/DatabricksGeometryTest.java @@ -16,23 +16,23 @@ public class DatabricksGeometryTest { public void testConstructor_WithValidPoint() throws DatabricksValidationException { DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 0); assertNotNull(geometry); - assertEquals("POINT(1 2)", geometry.getWkt()); - assertEquals(0, geometry.getSrid()); + assertEquals("POINT(1 2)", geometry.getWKT()); + assertEquals(0, geometry.getSRID()); } @Test public void testConstructor_WithValidPointAndSRID() throws DatabricksValidationException { DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 4326); assertNotNull(geometry); - assertEquals("POINT(1 2)", geometry.getWkt()); - assertEquals(4326, geometry.getSrid()); + assertEquals("POINT(1 2)", geometry.getWKT()); + assertEquals(4326, geometry.getSRID()); } @Test public void testConstructor_WithLineString() throws DatabricksValidationException { DatabricksGeometry geometry = new DatabricksGeometry("LINESTRING(0 0, 10 10, 20 20)", 0); assertNotNull(geometry); - assertTrue(geometry.getWkt().startsWith("LINESTRING")); + assertTrue(geometry.getWKT().startsWith("LINESTRING")); } @Test @@ -40,7 +40,7 @@ public void testConstructor_WithPolygon() throws DatabricksValidationException { DatabricksGeometry geometry = new DatabricksGeometry("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))", 0); assertNotNull(geometry); - assertTrue(geometry.getWkt().startsWith("POLYGON")); + assertTrue(geometry.getWKT().startsWith("POLYGON")); } @Test @@ -65,31 +65,31 @@ public void testConstructor_WithWhitespaceWKT_ThrowsException() { @Test public void testGetWkt() throws DatabricksValidationException { DatabricksGeometry geometry = new DatabricksGeometry("POINT(5 10)", 4326); - assertEquals("POINT(5 10)", geometry.getWkt()); + assertEquals("POINT(5 10)", geometry.getWKT()); } @Test public void testGetSrid_WithZero() throws DatabricksValidationException { DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 0); - assertEquals(0, geometry.getSrid()); + assertEquals(0, geometry.getSRID()); } @Test public void testGetSrid_With4326() throws DatabricksValidationException { DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 4326); - assertEquals(4326, geometry.getSrid()); + assertEquals(4326, geometry.getSRID()); } @Test public void testGetSrid_With3857() throws DatabricksValidationException { DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 3857); - assertEquals(3857, geometry.getSrid()); + assertEquals(3857, geometry.getSRID()); } @Test public void testGetWkb_ReturnsValidBytes() throws DatabricksValidationException { DatabricksGeometry geometry = new DatabricksGeometry("POINT(1 2)", 4326); - byte[] wkb = geometry.getWkb(); + byte[] wkb = geometry.getWKB(); assertNotNull(wkb); assertTrue(wkb.length > 0); } diff --git a/src/test/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverterTest.java b/src/test/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverterTest.java index 6abd48dd1b..258c38f487 100644 --- a/src/test/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverterTest.java +++ b/src/test/java/com/databricks/jdbc/api/impl/converters/ArrowToJavaObjectConverterTest.java @@ -558,7 +558,7 @@ public void testGeometryConversion() throws SQLException { assertInstanceOf(DatabricksGeometry.class, result); DatabricksGeometry geometry = (DatabricksGeometry) result; assertEquals("SRID=0;POINT(1 2)", geometry.toString()); - assertEquals(0, geometry.getSrid()); // Default SRID + assertEquals(0, geometry.getSRID()); // Default SRID varCharVector.close(); } @@ -577,7 +577,7 @@ public void testGeographyConversion() throws SQLException { assertInstanceOf(DatabricksGeography.class, result); DatabricksGeography geography = (DatabricksGeography) result; assertEquals("SRID=0;POINT(1 2)", geography.toString()); - assertEquals(0, geography.getSrid()); // Default SRID + assertEquals(0, geography.getSRID()); // Default SRID varCharVector.close(); } @@ -596,7 +596,7 @@ public void testGeometryWithSRIDConversion() throws SQLException { assertInstanceOf(DatabricksGeometry.class, result); DatabricksGeometry geometry = (DatabricksGeometry) result; assertEquals("SRID=4326;POINT(1 2)", geometry.toString()); - assertEquals(4326, geometry.getSrid()); + assertEquals(4326, geometry.getSRID()); varCharVector.close(); } @@ -615,7 +615,7 @@ public void testGeographyWithSRIDConversion() throws SQLException { assertInstanceOf(DatabricksGeography.class, result); DatabricksGeography geography = (DatabricksGeography) result; assertEquals("SRID=4326;POINT(1 2)", geography.toString()); - assertEquals(4326, geography.getSrid()); + assertEquals(4326, geography.getSRID()); varCharVector.close(); } diff --git a/src/test/java/com/databricks/jdbc/api/impl/converters/GeospatialConverterTest.java b/src/test/java/com/databricks/jdbc/api/impl/converters/GeospatialConverterTest.java index 3a54158190..22c8844cad 100644 --- a/src/test/java/com/databricks/jdbc/api/impl/converters/GeospatialConverterTest.java +++ b/src/test/java/com/databricks/jdbc/api/impl/converters/GeospatialConverterTest.java @@ -21,16 +21,16 @@ public class GeospatialConverterTest { public void testToDatabricksGeometry_WithValidWKT() throws DatabricksSQLException { DatabricksGeometry result = converter.toDatabricksGeometry("POINT(1 2)"); assertNotNull(result); - assertEquals("POINT(1 2)", result.getWkt()); - assertEquals(0, result.getSrid()); + assertEquals("POINT(1 2)", result.getWKT()); + assertEquals(0, result.getSRID()); } @Test public void testToDatabricksGeometry_WithEWKT() throws DatabricksSQLException { DatabricksGeometry result = converter.toDatabricksGeometry("SRID=4326;POINT(1 2)"); assertNotNull(result); - assertEquals("POINT(1 2)", result.getWkt()); - assertEquals(4326, result.getSrid()); + assertEquals("POINT(1 2)", result.getWKT()); + assertEquals(4326, result.getSRID()); } @Test @@ -38,7 +38,7 @@ public void testToDatabricksGeometry_WithTextInput() throws DatabricksSQLExcepti Text textInput = new Text("POINT(1 2)"); DatabricksGeometry result = converter.toDatabricksGeometry(textInput); assertNotNull(result); - assertEquals("POINT(1 2)", result.getWkt()); + assertEquals("POINT(1 2)", result.getWKT()); } @Test @@ -62,8 +62,8 @@ public void testToDatabricksGeometry_WithUnsupportedType_ThrowsException() { public void testToDatabricksGeography_WithValidWKT() throws DatabricksSQLException { DatabricksGeography result = converter.toDatabricksGeography("POINT(-122.4194 37.7749)"); assertNotNull(result); - assertEquals("POINT(-122.4194 37.7749)", result.getWkt()); - assertEquals(0, result.getSrid()); + assertEquals("POINT(-122.4194 37.7749)", result.getWKT()); + assertEquals(0, result.getSRID()); } @Test @@ -71,8 +71,8 @@ public void testToDatabricksGeography_WithEWKT() throws DatabricksSQLException { DatabricksGeography result = converter.toDatabricksGeography("SRID=4326;POINT(-122.4194 37.7749)"); assertNotNull(result); - assertEquals("POINT(-122.4194 37.7749)", result.getWkt()); - assertEquals(4326, result.getSrid()); + assertEquals("POINT(-122.4194 37.7749)", result.getWKT()); + assertEquals(4326, result.getSRID()); } @Test From 761bdee965ddd5291e564176f32a6f2916f9e120 Mon Sep 17 00:00:00 2001 From: Sreekanth Vadigi Date: Mon, 20 Oct 2025 14:06:00 +0530 Subject: [PATCH 11/14] addressing review comments - 6 Signed-off-by: Sreekanth Vadigi --- .../com/databricks/jdbc/api/IGeography.java | 6 +++ .../com/databricks/jdbc/api/IGeometry.java | 6 +++ .../jdbc/api/impl/DatabricksGeography.java | 3 +- .../jdbc/api/impl/DatabricksGeometry.java | 3 +- .../api/impl/converters/ConverterHelper.java | 1 + .../impl/converters/GeospatialConverter.java | 39 +++++++++---------- .../jdbc/common/util/DatabricksTypeUtil.java | 6 +-- 7 files changed, 38 insertions(+), 26 deletions(-) create mode 100644 src/main/java/com/databricks/jdbc/api/IGeography.java create mode 100644 src/main/java/com/databricks/jdbc/api/IGeometry.java diff --git a/src/main/java/com/databricks/jdbc/api/IGeography.java b/src/main/java/com/databricks/jdbc/api/IGeography.java new file mode 100644 index 0000000000..e36a824168 --- /dev/null +++ b/src/main/java/com/databricks/jdbc/api/IGeography.java @@ -0,0 +1,6 @@ +package com.databricks.jdbc.api; + +import com.databricks.jdbc.api.impl.IDatabricksGeospatial; + +/** Interface for GEOGRAPHY data types in Databricks JDBC driver. */ +public interface IGeography extends IDatabricksGeospatial {} diff --git a/src/main/java/com/databricks/jdbc/api/IGeometry.java b/src/main/java/com/databricks/jdbc/api/IGeometry.java new file mode 100644 index 0000000000..4eadb6b55f --- /dev/null +++ b/src/main/java/com/databricks/jdbc/api/IGeometry.java @@ -0,0 +1,6 @@ +package com.databricks.jdbc.api; + +import com.databricks.jdbc.api.impl.IDatabricksGeospatial; + +/** Interface for GEOMETRY data types in Databricks JDBC driver. */ +public interface IGeometry extends IDatabricksGeospatial {} diff --git a/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeography.java b/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeography.java index 2681dac401..20f148c563 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeography.java +++ b/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeography.java @@ -2,9 +2,10 @@ import static com.databricks.jdbc.common.util.DatabricksTypeUtil.GEOGRAPHY; +import com.databricks.jdbc.api.IGeography; import com.databricks.jdbc.exception.DatabricksValidationException; -public class DatabricksGeography extends AbstractDatabricksGeospatial { +public class DatabricksGeography extends AbstractDatabricksGeospatial implements IGeography { /** * Constructs a DatabricksGeography with the specified WKT and SRID. diff --git a/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeometry.java b/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeometry.java index e41535f560..b267e7dd84 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeometry.java +++ b/src/main/java/com/databricks/jdbc/api/impl/DatabricksGeometry.java @@ -2,9 +2,10 @@ import static com.databricks.jdbc.common.util.DatabricksTypeUtil.GEOMETRY; +import com.databricks.jdbc.api.IGeometry; import com.databricks.jdbc.exception.DatabricksValidationException; -public class DatabricksGeometry extends AbstractDatabricksGeospatial { +public class DatabricksGeometry extends AbstractDatabricksGeospatial implements IGeometry { /** * Constructs a DatabricksGeometry with the specified WKT and SRID. diff --git a/src/main/java/com/databricks/jdbc/api/impl/converters/ConverterHelper.java b/src/main/java/com/databricks/jdbc/api/impl/converters/ConverterHelper.java index d9b019bc78..dcefd5330a 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/converters/ConverterHelper.java +++ b/src/main/java/com/databricks/jdbc/api/impl/converters/ConverterHelper.java @@ -526,6 +526,7 @@ public static Object convertSqlTypeToSpecificJavaType( * @param columnSqlType The SQL type of the column, as defined in java.sql.Types * @return An ObjectConverter suitable for the specified SQL type */ + // TODO: replace all usages of this method with getConverterForColumnType public static ObjectConverter getConverterForSqlType(int columnSqlType) { return CONVERTER_CACHE.getOrDefault(columnSqlType, CONVERTER_CACHE.get(Types.VARCHAR)); } diff --git a/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java b/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java index 48259e85da..c601dc3caf 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java +++ b/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java @@ -1,5 +1,8 @@ package com.databricks.jdbc.api.impl.converters; +import static com.databricks.jdbc.common.util.DatabricksTypeUtil.GEOGRAPHY; +import static com.databricks.jdbc.common.util.DatabricksTypeUtil.GEOMETRY; + import com.databricks.jdbc.api.impl.DatabricksGeography; import com.databricks.jdbc.api.impl.DatabricksGeometry; import com.databricks.jdbc.api.impl.IDatabricksGeospatial; @@ -18,23 +21,7 @@ public DatabricksGeometry toDatabricksGeometry(Object object) throws DatabricksS if (object instanceof DatabricksGeometry) { return (DatabricksGeometry) object; } - - if (object instanceof String || object instanceof Text) { - String ewktString = object.toString(); - try { - int srid = WKTConverter.extractSRIDFromEWKT(ewktString); - String cleanWKT = WKTConverter.removeSRIDFromEWKT(ewktString); - return new DatabricksGeometry(cleanWKT, srid); - } catch (Exception e) { - String errorMessage = String.format("Failed to convert EWKT to geometry: %s", ewktString); - LOGGER.warn(errorMessage, e); - throw new DatabricksSQLException(errorMessage, e, DatabricksDriverErrorCode.INVALID_STATE); - } - } - - throw new DatabricksSQLException( - "Unsupported Geometry conversion from type: " + object.getClass(), - DatabricksDriverErrorCode.UNSUPPORTED_OPERATION); + return convertToGeospatial(object, GEOMETRY, DatabricksGeometry::new); } @Override @@ -42,25 +29,37 @@ public DatabricksGeography toDatabricksGeography(Object object) throws Databrick if (object instanceof DatabricksGeography) { return (DatabricksGeography) object; } + return convertToGeospatial(object, GEOGRAPHY, DatabricksGeography::new); + } + private T convertToGeospatial( + Object object, String typeName, GeospatialFactory factory) throws DatabricksSQLException { if (object instanceof String || object instanceof Text) { String ewktString = object.toString(); try { int srid = WKTConverter.extractSRIDFromEWKT(ewktString); String cleanWKT = WKTConverter.removeSRIDFromEWKT(ewktString); - return new DatabricksGeography(cleanWKT, srid); + return factory.create(cleanWKT, srid); } catch (Exception e) { - String errorMessage = String.format("Failed to convert EWKT to geography: %s", ewktString); + String errorMessage = + String.format("Failed to convert EWKT to %s: %s", typeName, ewktString); LOGGER.warn(errorMessage, e); throw new DatabricksSQLException(errorMessage, e, DatabricksDriverErrorCode.INVALID_STATE); } } throw new DatabricksSQLException( - "Unsupported Geography conversion from type: " + object.getClass(), + String.format( + "Unsupported %s conversion from type: %s", + typeName.substring(0, 1).toUpperCase() + typeName.substring(1), object.getClass()), DatabricksDriverErrorCode.UNSUPPORTED_OPERATION); } + @FunctionalInterface + private interface GeospatialFactory { + T create(String wkt, int srid) throws Exception; + } + @Override public String toString(Object object) throws DatabricksSQLException { if (object != null) { diff --git a/src/main/java/com/databricks/jdbc/common/util/DatabricksTypeUtil.java b/src/main/java/com/databricks/jdbc/common/util/DatabricksTypeUtil.java index f6529c946d..0610776a31 100644 --- a/src/main/java/com/databricks/jdbc/common/util/DatabricksTypeUtil.java +++ b/src/main/java/com/databricks/jdbc/common/util/DatabricksTypeUtil.java @@ -57,10 +57,8 @@ public class DatabricksTypeUtil { public static final String INTERVAL = "INTERVAL"; public static final String GEOMETRY = "GEOMETRY"; public static final String GEOGRAPHY = "GEOGRAPHY"; - public static final String GEOMETRY_CLASS_NAME = - "com.databricks.jdbc.api.impl.DatabricksGeometry"; - public static final String GEOGRAPHY_CLASS_NAME = - "com.databricks.jdbc.api.impl.DatabricksGeography"; + public static final String GEOMETRY_CLASS_NAME = "com.databricks.jdbc.api.IGeometry"; + public static final String GEOGRAPHY_CLASS_NAME = "com.databricks.jdbc.api.IGeography"; public static final String MEASURE = "measure"; private static final ArrayList SIGNED_TYPES = new ArrayList<>( From 585f9fc9cc4386d80698d4d50e815cd3afadc410 Mon Sep 17 00:00:00 2001 From: Sreekanth Vadigi Date: Mon, 20 Oct 2025 14:48:44 +0530 Subject: [PATCH 12/14] improving test coverage Signed-off-by: Sreekanth Vadigi --- .../api/impl/converters/WKTConverterTest.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/test/java/com/databricks/jdbc/api/impl/converters/WKTConverterTest.java b/src/test/java/com/databricks/jdbc/api/impl/converters/WKTConverterTest.java index feb29ca7a2..af2e45e6a9 100644 --- a/src/test/java/com/databricks/jdbc/api/impl/converters/WKTConverterTest.java +++ b/src/test/java/com/databricks/jdbc/api/impl/converters/WKTConverterTest.java @@ -124,6 +124,45 @@ public void testRemoveSRIDFromEWKT_OnlySRID() { assertEquals("", result); } + @Test + public void testToWKB_InvalidWKTFormat_LogsError() { + String invalidWkt = "POINT(1 2 3 4 5)"; // Too many coordinates for a POINT + + DatabricksValidationException exception = + assertThrows(DatabricksValidationException.class, () -> WKTConverter.toWKB(invalidWkt)); + + assertTrue(exception.getMessage().contains("Invalid WKT format")); + } + + @Test + public void testToWKT_ValidWKB() throws DatabricksValidationException { + // First create valid WKB from WKT + byte[] wkb = WKTConverter.toWKB("POINT (1 2)"); + String wkt = WKTConverter.toWKT(wkb); + + assertEquals("POINT (1 2)", wkt); + } + + @Test + public void testToWKT_NullWKB() { + assertThrows(DatabricksValidationException.class, () -> WKTConverter.toWKT(null)); + } + + @Test + public void testToWKT_EmptyWKB() { + assertThrows(DatabricksValidationException.class, () -> WKTConverter.toWKT(new byte[0])); + } + + @Test + public void testToWKT_InvalidWKB_LogsError() { + byte[] invalidWkb = new byte[] {1, 2, 3, 4, 5}; // Random invalid bytes + + DatabricksValidationException exception = + assertThrows(DatabricksValidationException.class, () -> WKTConverter.toWKT(invalidWkb)); + + assertTrue(exception.getMessage().contains("Invalid WKB format")); + } + @Test public void testConcurrency() throws Exception { int numThreads = 50; From ea2f8fad6219be35e1539b476c813dc1cc2da88b Mon Sep 17 00:00:00 2001 From: Sreekanth Vadigi Date: Fri, 24 Oct 2025 12:36:44 +0530 Subject: [PATCH 13/14] moving interface out of impl Signed-off-by: Sreekanth Vadigi --- .../databricks/jdbc/api/{impl => }/IDatabricksGeospatial.java | 2 +- src/main/java/com/databricks/jdbc/api/IGeography.java | 2 -- src/main/java/com/databricks/jdbc/api/IGeometry.java | 2 -- .../databricks/jdbc/api/impl/AbstractDatabricksGeospatial.java | 1 + .../jdbc/api/impl/converters/GeospatialConverter.java | 2 +- 5 files changed, 3 insertions(+), 6 deletions(-) rename src/main/java/com/databricks/jdbc/api/{impl => }/IDatabricksGeospatial.java (98%) diff --git a/src/main/java/com/databricks/jdbc/api/impl/IDatabricksGeospatial.java b/src/main/java/com/databricks/jdbc/api/IDatabricksGeospatial.java similarity index 98% rename from src/main/java/com/databricks/jdbc/api/impl/IDatabricksGeospatial.java rename to src/main/java/com/databricks/jdbc/api/IDatabricksGeospatial.java index 2197343370..d58f8d9f2b 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/IDatabricksGeospatial.java +++ b/src/main/java/com/databricks/jdbc/api/IDatabricksGeospatial.java @@ -1,4 +1,4 @@ -package com.databricks.jdbc.api.impl; +package com.databricks.jdbc.api; import com.databricks.jdbc.exception.DatabricksValidationException; diff --git a/src/main/java/com/databricks/jdbc/api/IGeography.java b/src/main/java/com/databricks/jdbc/api/IGeography.java index e36a824168..bae749bc52 100644 --- a/src/main/java/com/databricks/jdbc/api/IGeography.java +++ b/src/main/java/com/databricks/jdbc/api/IGeography.java @@ -1,6 +1,4 @@ package com.databricks.jdbc.api; -import com.databricks.jdbc.api.impl.IDatabricksGeospatial; - /** Interface for GEOGRAPHY data types in Databricks JDBC driver. */ public interface IGeography extends IDatabricksGeospatial {} diff --git a/src/main/java/com/databricks/jdbc/api/IGeometry.java b/src/main/java/com/databricks/jdbc/api/IGeometry.java index 4eadb6b55f..850eb3af55 100644 --- a/src/main/java/com/databricks/jdbc/api/IGeometry.java +++ b/src/main/java/com/databricks/jdbc/api/IGeometry.java @@ -1,6 +1,4 @@ package com.databricks.jdbc.api; -import com.databricks.jdbc.api.impl.IDatabricksGeospatial; - /** Interface for GEOMETRY data types in Databricks JDBC driver. */ public interface IGeometry extends IDatabricksGeospatial {} diff --git a/src/main/java/com/databricks/jdbc/api/impl/AbstractDatabricksGeospatial.java b/src/main/java/com/databricks/jdbc/api/impl/AbstractDatabricksGeospatial.java index a344973e38..eba7bc4fd4 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/AbstractDatabricksGeospatial.java +++ b/src/main/java/com/databricks/jdbc/api/impl/AbstractDatabricksGeospatial.java @@ -1,5 +1,6 @@ package com.databricks.jdbc.api.impl; +import com.databricks.jdbc.api.IDatabricksGeospatial; import com.databricks.jdbc.api.impl.converters.WKTConverter; import com.databricks.jdbc.exception.DatabricksValidationException; import com.databricks.jdbc.log.JdbcLogger; diff --git a/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java b/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java index c601dc3caf..69d99e63bf 100644 --- a/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java +++ b/src/main/java/com/databricks/jdbc/api/impl/converters/GeospatialConverter.java @@ -3,9 +3,9 @@ import static com.databricks.jdbc.common.util.DatabricksTypeUtil.GEOGRAPHY; import static com.databricks.jdbc.common.util.DatabricksTypeUtil.GEOMETRY; +import com.databricks.jdbc.api.IDatabricksGeospatial; import com.databricks.jdbc.api.impl.DatabricksGeography; import com.databricks.jdbc.api.impl.DatabricksGeometry; -import com.databricks.jdbc.api.impl.IDatabricksGeospatial; import com.databricks.jdbc.exception.DatabricksSQLException; import com.databricks.jdbc.log.JdbcLogger; import com.databricks.jdbc.log.JdbcLoggerFactory; From 50f27cc35bf775a73aa581e70e9eca9820ccd080 Mon Sep 17 00:00:00 2001 From: Sreekanth Vadigi Date: Fri, 24 Oct 2025 14:48:02 +0530 Subject: [PATCH 14/14] skipping thrift server fake service test (doesn't support geospatial on the current version) Signed-off-by: Sreekanth Vadigi --- .../tests/DataTypesIntegrationTests.java | 8 ++++ ...-27a67ec3-b459-49cd-9d6f-f2cdf5ecd35a.json | 33 +++++++++++++++++ ...-a917e9cc-d745-4586-97f4-d6d4701804fd.json | 32 ++++++++++++++++ ...-2633ce3d-b2d7-44ce-b0c1-fc3ba4fc18ec.json | 34 +++++++++++++++++ ...-293a9649-9cf5-48ed-820e-e9177e852c1a.json | 36 ++++++++++++++++++ ...-33b87d3b-3ae5-4f26-aefd-f74b47230318.json | 34 +++++++++++++++++ ...-535e7132-d129-4dae-9556-fb04bf7e09ac.json | 37 +++++++++++++++++++ 7 files changed, 214 insertions(+) create mode 100644 src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/oidc_.well-known_oauth-authorization-server-27a67ec3-b459-49cd-9d6f-f2cdf5ecd35a.json create mode 100644 src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/oidc_.well-known_oauth-authorization-server-a917e9cc-d745-4586-97f4-d6d4701804fd.json create mode 100644 src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/sql_protocolv1_o_6051921418418893_0819-204509-hill72-2633ce3d-b2d7-44ce-b0c1-fc3ba4fc18ec.json create mode 100644 src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/sql_protocolv1_o_6051921418418893_0819-204509-hill72-293a9649-9cf5-48ed-820e-e9177e852c1a.json create mode 100644 src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/sql_protocolv1_o_6051921418418893_0819-204509-hill72-33b87d3b-3ae5-4f26-aefd-f74b47230318.json create mode 100644 src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/sql_protocolv1_o_6051921418418893_0819-204509-hill72-535e7132-d129-4dae-9556-fb04bf7e09ac.json diff --git a/src/test/java/com/databricks/jdbc/integration/fakeservice/tests/DataTypesIntegrationTests.java b/src/test/java/com/databricks/jdbc/integration/fakeservice/tests/DataTypesIntegrationTests.java index f4a03c6f44..7809278eac 100644 --- a/src/test/java/com/databricks/jdbc/integration/fakeservice/tests/DataTypesIntegrationTests.java +++ b/src/test/java/com/databricks/jdbc/integration/fakeservice/tests/DataTypesIntegrationTests.java @@ -11,6 +11,7 @@ import java.util.Properties; import java.util.stream.Stream; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -340,6 +341,13 @@ private void validateIntervalResults(ResultSet resultSet) throws SQLException { @Test void testGeospatialTypes() throws SQLException { + + // Skip for THRIFT_SERVER as the test environment version doesn't support geospatial types + // TODO: Update stubs and remove this skip once THRIFT_SERVER environment is upgraded to support + // geospatial types + Assumptions.assumeTrue( + isSqlExecSdkClient(), "Geospatial types are not supported on THRIFT_SERVER yet"); + String query = "SELECT * FROM (VALUES " + "(1, ST_GeomFromText('POINT (1 2)'), ST_GeogFromText('POINT (3 4)')), " diff --git a/src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/oidc_.well-known_oauth-authorization-server-27a67ec3-b459-49cd-9d6f-f2cdf5ecd35a.json b/src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/oidc_.well-known_oauth-authorization-server-27a67ec3-b459-49cd-9d6f-f2cdf5ecd35a.json new file mode 100644 index 0000000000..da6984e000 --- /dev/null +++ b/src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/oidc_.well-known_oauth-authorization-server-27a67ec3-b459-49cd-9d6f-f2cdf5ecd35a.json @@ -0,0 +1,33 @@ +{ + "id" : "27a67ec3-b459-49cd-9d6f-f2cdf5ecd35a", + "name" : "oidc_.well-known_oauth-authorization-server", + "request" : { + "url" : "/oidc/.well-known/oauth-authorization-server", + "method" : "GET" + }, + "response" : { + "status" : 200, + "body" : "{\"authorization_endpoint\":\"https:\\/\\/e2-dogfood.staging.cloud.databricks.com\\/oidc\\/v1\\/authorize\",\"token_endpoint\":\"https:\\/\\/e2-dogfood.staging.cloud.databricks.com\\/oidc\\/v1\\/token\",\"issuer\":\"https:\\/\\/e2-dogfood.staging.cloud.databricks.com\\/oidc\",\"jwks_uri\":\"https:\\/\\/oregon.staging.cloud.databricks.com\\/oidc\\/jwks.json\",\"scopes_supported\":[\"all-apis\",\"apps.apps\",\"catalog.artifact-allowlists\",\"catalog.catalogs\",\"catalog.connections\",\"catalog.credentials\",\"catalog.entity-tag-assignments\",\"catalog.external-lineage\",\"catalog.external-locations\",\"catalog.external-metadata\",\"catalog.functions\",\"catalog.grants\",\"catalog.metastores\",\"catalog.model-versions\",\"catalog.online-tables\",\"catalog.policies\",\"catalog.quality-monitors\",\"catalog.registered-models\",\"catalog.resource-quotas\",\"catalog.rfa\",\"catalog.schemas\",\"catalog.storage-credentials\",\"catalog.system-schemas\",\"catalog.table-constraints\",\"catalog.tables\",\"catalog.temporary-path-credentials\",\"catalog.temporary-table-credentials\",\"catalog.volumes\",\"catalog.workspace-bindings\",\"cleanrooms.clean-room-asset-revisions\",\"cleanrooms.clean-room-assets\",\"cleanrooms.clean-room-auto-approval-rules\",\"cleanrooms.clean-room-task-runs\",\"cleanrooms.clean-rooms\",\"compute.cluster-policies\",\"compute.clusters\",\"compute.command-execution\",\"compute.global-init-scripts\",\"compute.instance-pools\",\"compute.instance-profiles\",\"compute.libraries\",\"compute.policy-compliance-for-clusters\",\"compute.policy-families\",\"dashboards.genie\",\"dashboards.lakeview\",\"dashboards.lakeview-embedded\",\"database.database\",\"dataquality.data-quality\",\"email\",\"files.dbfs\",\"files.files\",\"iam.account-access-control-proxy\",\"iam.current-user\",\"iam.groups\",\"iam.permissions\",\"iam.service-principals\",\"iam.users\",\"iamv2.iam\",\"jobs.jobs\",\"jobs.policy-compliance-for-jobs\",\"marketplace.consumer-fulfillments\",\"marketplace.consumer-installations\",\"marketplace.consumer-listings\",\"marketplace.consumer-personalization-requests\",\"marketplace.consumer-providers\",\"marketplace.provider-exchange-filters\",\"marketplace.provider-exchanges\",\"marketplace.provider-files\",\"marketplace.provider-listings\",\"marketplace.provider-personalization-requests\",\"marketplace.provider-provider-analytics-dashboards\",\"marketplace.provider-providers\",\"mcp.external\",\"mcp.functions\",\"mcp.genie\",\"mcp.sql\",\"mcp.vectorsearch\",\"ml.experiments\",\"ml.model-registry\",\"oauth2.service-principal-secrets-proxy\",\"offline_access\",\"openid\",\"pipelines.pipelines\",\"profile\",\"qualitymonitorv2.quality-monitor\",\"serving.serving-endpoints\",\"settings.aibi-dashboard-embedding-access-policy\",\"settings.aibi-dashboard-embedding-approved-domains\",\"settings.automatic-cluster-update\",\"settings.compliance-security-profile\",\"settings.dashboard-email-subscriptions\",\"settings.default-namespace\",\"settings.disable-legacy-access\",\"settings.disable-legacy-dbfs\",\"settings.enable-export-notebook\",\"settings.enable-notebook-table-clipboard\",\"settings.enable-results-downloading\",\"settings.enhanced-security-monitoring\",\"settings.ip-access-lists\",\"settings.notification-destinations\",\"settings.restrict-workspace-admins\",\"settings.sql-results-download\",\"settings.token-management\",\"settings.tokens\",\"settings.workspace-conf\",\"settingsv2.settings\",\"sharing.providers\",\"sharing.recipient-activation\",\"sharing.recipient-federation-policies\",\"sharing.recipients\",\"sharing.shares\",\"sql\",\"sql.alerts\",\"sql.alerts-legacy\",\"sql.alerts-v2\",\"sql.dashboards\",\"sql.data-sources\",\"sql.dbsql-permissions\",\"sql.driver\",\"sql.queries\",\"sql.queries-legacy\",\"sql.query-history\",\"sql.statement-execution\",\"sql.warehouses\",\"tags.tag-policies\",\"vectorsearch.vector-search-endpoints\",\"vectorsearch.vector-search-indexes\",\"workspace.git-credentials\",\"workspace.repos\",\"workspace.secrets\",\"workspace.workspace\"],\"response_types_supported\":[\"code\",\"id_token\"],\"response_modes_supported\":[\"query\",\"fragment\",\"form_post\"],\"grant_types_supported\":[\"client_credentials\",\"authorization_code\",\"refresh_token\"],\"code_challenge_methods_supported\":[\"S256\"],\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"none\"],\"subject_types_supported\":[\"public\"],\"id_token_signing_alg_values_supported\":[\"RS256\"],\"claims_supported\":[\"iss\",\"sub\",\"aud\",\"iat\",\"exp\",\"jti\",\"name\",\"family_name\",\"given_name\",\"preferred_username\"],\"request_uri_parameter_supported\":false}", + "headers" : { + "x-request-id" : "6398af77-8a27-4154-b641-264b2a96e878", + "date" : "Fri, 24 Oct 2025 09:13:20 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "vary" : "Accept-Encoding", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json; charset=UTF-8", + "server-timing" : "request_id;dur=0;desc=\"6398af77-8a27-4154-b641-264b2a96e878\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "27a67ec3-b459-49cd-9d6f-f2cdf5ecd35a", + "scenarioName" : "scenario-2-oidc-.well-known-oauth-authorization-server", + "requiredScenarioState" : "Started", + "newScenarioState" : "scenario-2-oidc-.well-known-oauth-authorization-server-2", + "insertionIndex" : 6 +} \ No newline at end of file diff --git a/src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/oidc_.well-known_oauth-authorization-server-a917e9cc-d745-4586-97f4-d6d4701804fd.json b/src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/oidc_.well-known_oauth-authorization-server-a917e9cc-d745-4586-97f4-d6d4701804fd.json new file mode 100644 index 0000000000..3c97117b70 --- /dev/null +++ b/src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/oidc_.well-known_oauth-authorization-server-a917e9cc-d745-4586-97f4-d6d4701804fd.json @@ -0,0 +1,32 @@ +{ + "id" : "a917e9cc-d745-4586-97f4-d6d4701804fd", + "name" : "oidc_.well-known_oauth-authorization-server", + "request" : { + "url" : "/oidc/.well-known/oauth-authorization-server", + "method" : "GET" + }, + "response" : { + "status" : 200, + "body" : "{\"authorization_endpoint\":\"https:\\/\\/e2-dogfood.staging.cloud.databricks.com\\/oidc\\/v1\\/authorize\",\"token_endpoint\":\"https:\\/\\/e2-dogfood.staging.cloud.databricks.com\\/oidc\\/v1\\/token\",\"issuer\":\"https:\\/\\/e2-dogfood.staging.cloud.databricks.com\\/oidc\",\"jwks_uri\":\"https:\\/\\/oregon.staging.cloud.databricks.com\\/oidc\\/jwks.json\",\"scopes_supported\":[\"all-apis\",\"apps.apps\",\"catalog.artifact-allowlists\",\"catalog.catalogs\",\"catalog.connections\",\"catalog.credentials\",\"catalog.entity-tag-assignments\",\"catalog.external-lineage\",\"catalog.external-locations\",\"catalog.external-metadata\",\"catalog.functions\",\"catalog.grants\",\"catalog.metastores\",\"catalog.model-versions\",\"catalog.online-tables\",\"catalog.policies\",\"catalog.quality-monitors\",\"catalog.registered-models\",\"catalog.resource-quotas\",\"catalog.rfa\",\"catalog.schemas\",\"catalog.storage-credentials\",\"catalog.system-schemas\",\"catalog.table-constraints\",\"catalog.tables\",\"catalog.temporary-path-credentials\",\"catalog.temporary-table-credentials\",\"catalog.volumes\",\"catalog.workspace-bindings\",\"cleanrooms.clean-room-asset-revisions\",\"cleanrooms.clean-room-assets\",\"cleanrooms.clean-room-auto-approval-rules\",\"cleanrooms.clean-room-task-runs\",\"cleanrooms.clean-rooms\",\"compute.cluster-policies\",\"compute.clusters\",\"compute.command-execution\",\"compute.global-init-scripts\",\"compute.instance-pools\",\"compute.instance-profiles\",\"compute.libraries\",\"compute.policy-compliance-for-clusters\",\"compute.policy-families\",\"dashboards.genie\",\"dashboards.lakeview\",\"dashboards.lakeview-embedded\",\"database.database\",\"dataquality.data-quality\",\"email\",\"files.dbfs\",\"files.files\",\"iam.account-access-control-proxy\",\"iam.current-user\",\"iam.groups\",\"iam.permissions\",\"iam.service-principals\",\"iam.users\",\"iamv2.iam\",\"jobs.jobs\",\"jobs.policy-compliance-for-jobs\",\"marketplace.consumer-fulfillments\",\"marketplace.consumer-installations\",\"marketplace.consumer-listings\",\"marketplace.consumer-personalization-requests\",\"marketplace.consumer-providers\",\"marketplace.provider-exchange-filters\",\"marketplace.provider-exchanges\",\"marketplace.provider-files\",\"marketplace.provider-listings\",\"marketplace.provider-personalization-requests\",\"marketplace.provider-provider-analytics-dashboards\",\"marketplace.provider-providers\",\"mcp.external\",\"mcp.functions\",\"mcp.genie\",\"mcp.sql\",\"mcp.vectorsearch\",\"ml.experiments\",\"ml.model-registry\",\"oauth2.service-principal-secrets-proxy\",\"offline_access\",\"openid\",\"pipelines.pipelines\",\"profile\",\"qualitymonitorv2.quality-monitor\",\"serving.serving-endpoints\",\"settings.aibi-dashboard-embedding-access-policy\",\"settings.aibi-dashboard-embedding-approved-domains\",\"settings.automatic-cluster-update\",\"settings.compliance-security-profile\",\"settings.dashboard-email-subscriptions\",\"settings.default-namespace\",\"settings.disable-legacy-access\",\"settings.disable-legacy-dbfs\",\"settings.enable-export-notebook\",\"settings.enable-notebook-table-clipboard\",\"settings.enable-results-downloading\",\"settings.enhanced-security-monitoring\",\"settings.ip-access-lists\",\"settings.notification-destinations\",\"settings.restrict-workspace-admins\",\"settings.sql-results-download\",\"settings.token-management\",\"settings.tokens\",\"settings.workspace-conf\",\"settingsv2.settings\",\"sharing.providers\",\"sharing.recipient-activation\",\"sharing.recipient-federation-policies\",\"sharing.recipients\",\"sharing.shares\",\"sql\",\"sql.alerts\",\"sql.alerts-legacy\",\"sql.alerts-v2\",\"sql.dashboards\",\"sql.data-sources\",\"sql.dbsql-permissions\",\"sql.driver\",\"sql.queries\",\"sql.queries-legacy\",\"sql.query-history\",\"sql.statement-execution\",\"sql.warehouses\",\"tags.tag-policies\",\"vectorsearch.vector-search-endpoints\",\"vectorsearch.vector-search-indexes\",\"workspace.git-credentials\",\"workspace.repos\",\"workspace.secrets\",\"workspace.workspace\"],\"response_types_supported\":[\"code\",\"id_token\"],\"response_modes_supported\":[\"query\",\"fragment\",\"form_post\"],\"grant_types_supported\":[\"client_credentials\",\"authorization_code\",\"refresh_token\"],\"code_challenge_methods_supported\":[\"S256\"],\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"none\"],\"subject_types_supported\":[\"public\"],\"id_token_signing_alg_values_supported\":[\"RS256\"],\"claims_supported\":[\"iss\",\"sub\",\"aud\",\"iat\",\"exp\",\"jti\",\"name\",\"family_name\",\"given_name\",\"preferred_username\"],\"request_uri_parameter_supported\":false}", + "headers" : { + "x-request-id" : "2ea66a34-ef7a-4d2e-80ca-0e188dd6cfe0", + "date" : "Fri, 24 Oct 2025 09:13:22 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "vary" : "Accept-Encoding", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/json; charset=UTF-8", + "server-timing" : "request_id;dur=0;desc=\"2ea66a34-ef7a-4d2e-80ca-0e188dd6cfe0\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "a917e9cc-d745-4586-97f4-d6d4701804fd", + "scenarioName" : "scenario-2-oidc-.well-known-oauth-authorization-server", + "requiredScenarioState" : "scenario-2-oidc-.well-known-oauth-authorization-server-2", + "insertionIndex" : 4 +} \ No newline at end of file diff --git a/src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/sql_protocolv1_o_6051921418418893_0819-204509-hill72-2633ce3d-b2d7-44ce-b0c1-fc3ba4fc18ec.json b/src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/sql_protocolv1_o_6051921418418893_0819-204509-hill72-2633ce3d-b2d7-44ce-b0c1-fc3ba4fc18ec.json new file mode 100644 index 0000000000..0e610868a9 --- /dev/null +++ b/src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/sql_protocolv1_o_6051921418418893_0819-204509-hill72-2633ce3d-b2d7-44ce-b0c1-fc3ba4fc18ec.json @@ -0,0 +1,34 @@ +{ + "id" : "2633ce3d-b2d7-44ce-b0c1-fc3ba4fc18ec", + "name" : "sql_protocolv1_o_6051921418418893_0819-204509-hill72", + "request" : { + "url" : "/sql/protocolv1/o/6051921418418893/0819-204509-hill72", + "method" : "POST", + "bodyPatterns" : [ { + "binaryEqualTo" : "gAEAAQAAAAxDbG9zZVNlc3Npb24AAAACDAABDAABDAABCwABAAAAEAnNQTevcEGqkAQucLP7Kb0LAAIAAAAQ4AypzuZjTMObzvkAoICHTgAAAAA=" + } ] + }, + "response" : { + "status" : 200, + "base64Body" : "gAEAAgAAAAxDbG9zZVNlc3Npb24AAAACDAAADAABCAABAAAAAAAAAA==", + "headers" : { + "x-request-id" : "e00003a5-f35d-4333-aa14-22a30eede082", + "date" : "Fri, 24 Oct 2025 09:13:25 GMT,Fri, 24 Oct 2025 09:13:25 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "x-frame-options" : "SAMEORIGIN", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-xss-protection" : "1; mode=block", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/x-thrift", + "server-timing" : "request_id;dur=0;desc=\"e00003a5-f35d-4333-aa14-22a30eede082\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "2633ce3d-b2d7-44ce-b0c1-fc3ba4fc18ec", + "insertionIndex" : 1 +} \ No newline at end of file diff --git a/src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/sql_protocolv1_o_6051921418418893_0819-204509-hill72-293a9649-9cf5-48ed-820e-e9177e852c1a.json b/src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/sql_protocolv1_o_6051921418418893_0819-204509-hill72-293a9649-9cf5-48ed-820e-e9177e852c1a.json new file mode 100644 index 0000000000..ff712dbe7b --- /dev/null +++ b/src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/sql_protocolv1_o_6051921418418893_0819-204509-hill72-293a9649-9cf5-48ed-820e-e9177e852c1a.json @@ -0,0 +1,36 @@ +{ + "id" : "293a9649-9cf5-48ed-820e-e9177e852c1a", + "name" : "sql_protocolv1_o_6051921418418893_0819-204509-hill72", + "request" : { + "url" : "/sql/protocolv1/o/6051921418418893/0819-204509-hill72", + "method" : "POST", + "bodyPatterns" : [ { + "binaryEqualTo" : "gAEAAQAAAAtPcGVuU2Vzc2lvbgAAAAEMAAEIAAH////5DQAECwsAAAAACgUCAAAAAAAApQkMBQQLAAEAAAAFU1BBUksLAAIAAAAHZGVmYXVsdAACBQUBAAA=" + } ] + }, + "response" : { + "status" : 200, + "base64Body" : "gAEAAgAAAAtPcGVuU2Vzc2lvbgAAAAEMAAAMAAEIAAEAAAAAAAgAAgAApQgMAAMMAAELAAEAAAAQCc1BN69wQaqQBC5ws/spvQsAAgAAABDgDKnO5mNMw5vO+QCggIdOBg0BAAAACA0BAAClCAANAAQLCwAAAAAMBQQLAAEAAAAFc3BhcmsLAAIAAAAHZGVmYXVsdAACBQUBAAA=", + "headers" : { + "x-request-id" : "c30cf2c5-0788-4f11-959d-0cbf52033345", + "date" : "Fri, 24 Oct 2025 09:13:23 GMT,Fri, 24 Oct 2025 09:13:23 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "x-frame-options" : "SAMEORIGIN", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-xss-protection" : "1; mode=block", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/x-thrift", + "server-timing" : "request_id;dur=0;desc=\"c30cf2c5-0788-4f11-959d-0cbf52033345\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "293a9649-9cf5-48ed-820e-e9177e852c1a", + "scenarioName" : "scenario-1-sql-protocolv1-o-6051921418418893-0819-204509-hill72", + "requiredScenarioState" : "scenario-1-sql-protocolv1-o-6051921418418893-0819-204509-hill72-2", + "insertionIndex" : 3 +} \ No newline at end of file diff --git a/src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/sql_protocolv1_o_6051921418418893_0819-204509-hill72-33b87d3b-3ae5-4f26-aefd-f74b47230318.json b/src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/sql_protocolv1_o_6051921418418893_0819-204509-hill72-33b87d3b-3ae5-4f26-aefd-f74b47230318.json new file mode 100644 index 0000000000..0f5bcbc188 --- /dev/null +++ b/src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/sql_protocolv1_o_6051921418418893_0819-204509-hill72-33b87d3b-3ae5-4f26-aefd-f74b47230318.json @@ -0,0 +1,34 @@ +{ + "id" : "33b87d3b-3ae5-4f26-aefd-f74b47230318", + "name" : "sql_protocolv1_o_6051921418418893_0819-204509-hill72", + "request" : { + "url" : "/sql/protocolv1/o/6051921418418893/0819-204509-hill72", + "method" : "POST", + "bodyPatterns" : [ { + "binaryEqualTo" : "gAEAAQAAAAxDbG9zZVNlc3Npb24AAAACDAABDAABDAABCwABAAAAEHT3AdS4M0fbvr9VNZLKF4QLAAIAAAAQuhmVKxnqTW+o2AcGqJWHnQAAAAA=" + } ] + }, + "response" : { + "status" : 200, + "base64Body" : "gAEAAgAAAAxDbG9zZVNlc3Npb24AAAACDAAADAABCAABAAAAAAAAAA==", + "headers" : { + "x-request-id" : "7c4d98c5-9310-444a-8d60-71b75871ebbc", + "date" : "Fri, 24 Oct 2025 09:13:24 GMT,Fri, 24 Oct 2025 09:13:24 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "x-frame-options" : "SAMEORIGIN", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-xss-protection" : "1; mode=block", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/x-thrift", + "server-timing" : "request_id;dur=0;desc=\"7c4d98c5-9310-444a-8d60-71b75871ebbc\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "33b87d3b-3ae5-4f26-aefd-f74b47230318", + "insertionIndex" : 2 +} \ No newline at end of file diff --git a/src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/sql_protocolv1_o_6051921418418893_0819-204509-hill72-535e7132-d129-4dae-9556-fb04bf7e09ac.json b/src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/sql_protocolv1_o_6051921418418893_0819-204509-hill72-535e7132-d129-4dae-9556-fb04bf7e09ac.json new file mode 100644 index 0000000000..4b452253d5 --- /dev/null +++ b/src/test/resources/thriftserverapi/datatypesintegrationtests/testgeospatialtypes/mappings/sql_protocolv1_o_6051921418418893_0819-204509-hill72-535e7132-d129-4dae-9556-fb04bf7e09ac.json @@ -0,0 +1,37 @@ +{ + "id" : "535e7132-d129-4dae-9556-fb04bf7e09ac", + "name" : "sql_protocolv1_o_6051921418418893_0819-204509-hill72", + "request" : { + "url" : "/sql/protocolv1/o/6051921418418893/0819-204509-hill72", + "method" : "POST", + "bodyPatterns" : [ { + "binaryEqualTo" : "gAEAAQAAAAtPcGVuU2Vzc2lvbgAAAAEMAAEIAAH////5DQAECwsAAAAACgUCAAAAAAAApQkMBQQLAAEAAAAFU1BBUksLAAIAAAAHZGVmYXVsdAACBQUBAAA=" + } ] + }, + "response" : { + "status" : 200, + "base64Body" : "gAEAAgAAAAtPcGVuU2Vzc2lvbgAAAAEMAAAMAAEIAAEAAAAAAAgAAgAApQgMAAMMAAELAAEAAAAQdPcB1LgzR9u+v1U1ksoXhAsAAgAAABC6GZUrGepNb6jYBwaolYedBg0BAAAACA0BAAClCAANAAQLCwAAAAAMBQQLAAEAAAAFc3BhcmsLAAIAAAAHZGVmYXVsdAACBQUBAAA=", + "headers" : { + "x-request-id" : "434b02f0-1e52-4dbd-afc4-8abc17cdd547", + "date" : "Fri, 24 Oct 2025 09:13:21 GMT,Fri, 24 Oct 2025 09:13:21 GMT", + "server" : "databricks", + "x-databricks-popp-response-code-details" : "via_upstream", + "x-databricks-shard-debug" : "oregon-staging", + "x-frame-options" : "SAMEORIGIN", + "x-databricks-org-id" : "6051921418418893", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-content-type-options" : "nosniff", + "x-xss-protection" : "1; mode=block", + "x-databricks-popp-routing-reason" : "deployment-name", + "content-type" : "application/x-thrift", + "server-timing" : "request_id;dur=0;desc=\"434b02f0-1e52-4dbd-afc4-8abc17cdd547\", client_protocol;dur=0;desc=\"HTTP/1.1\"", + "alt-svc" : "h3=\":5443\"; ma=86400, h3-29=\":5443\"; ma=86400", + "x-databricks-apiproxy-response-code-details" : "via_upstream" + } + }, + "uuid" : "535e7132-d129-4dae-9556-fb04bf7e09ac", + "scenarioName" : "scenario-1-sql-protocolv1-o-6051921418418893-0819-204509-hill72", + "requiredScenarioState" : "Started", + "newScenarioState" : "scenario-1-sql-protocolv1-o-6051921418418893-0819-204509-hill72-2", + "insertionIndex" : 5 +} \ No newline at end of file