Skip to content

Commit f2b2453

Browse files
Fix driver crash when using INTERVAL types (databricks#1085)
## Description This PR fixes a driver crash that occurs when executing a query returning an `INTERVAL` type mentioned in databricks#1083 issue. Previously, `PreparedStatement.getMetaData()` failed with: ``` IllegalArgumentException: No enum constant ColumnInfoTypeName.INTERVAL DAY TO SECOND ``` This happened because the driver attempted to map interval types to an enum that did not support multi-word SQL type names such as `INTERVAL DAY TO SECOND`. ### What has been done * Added support for INTERVAL types in metadata handling. * Ensured `DatabricksPreparedStatement.getMetaData()` and supporting code paths correctly parse and map interval type names. * Changed visibility of `SIGNED_TYPES` from private to public (just a suggestion — it seems useful and potentially reusable). This PR addresses the issue described in *Follow-up databricks#1064* and fixes the metadata retrieval for interval expressions such as: ```sql SELECT current_timestamp() - '2025-01-01 00:00:00.0' SELECT INTERVAL '15' MINUTE ``` --- ## Testing ~**Still planning to add automated tests — this is a DRAFT.**~ Manual testing performed so far: * Used a small Java program (included below) that: * Prepares a statement returning an interval value. * Calls `getMetaData()` to verify that no exceptions are thrown. * Prints metadata fields (type, name, precision, scale, etc.). * Executes the query and validates that values can be retrieved via both `getObject()` and `getString()`. Tested on driver version **3.0.4**. Before this fix → driver crashes. With this PR → metadata is returned successfully and the interval value can be fetched. ### How to manually test the change You can use the following standalone Java snippet to test the fix end-to-end: ```java public static void main(String[] args) { String url = "..."; Properties props = new Properties(); Driver driver = Driver.getInstance(); try (Connection conn = driver.connect(url, props)) { try (PreparedStatement st = conn.prepareStatement("SELECT INTERVAL '15' MINUTE")) { ResultSetMetaData rsmd = st.getMetaData(); System.out.println("=== ResultSetMetaData ==="); System.out.println("Column count: " + rsmd.getColumnCount()); for (int i = 1; i <= rsmd.getColumnCount(); i++) { System.out.println("\nColumn " + i + ":"); System.out.println(" Name: " + rsmd.getColumnName(i)); System.out.println(" Label: " + rsmd.getColumnLabel(i)); System.out.println(" Type: " + rsmd.getColumnType(i)); System.out.println(" Type Name: " + rsmd.getColumnTypeName(i)); System.out.println(" Class Name: " + rsmd.getColumnClassName(i)); System.out.println(" Display Size: " + rsmd.getColumnDisplaySize(i)); System.out.println(" Precision: " + rsmd.getPrecision(i)); System.out.println(" Scale: " + rsmd.getScale(i)); } try (ResultSet rs = st.executeQuery()) { System.out.println("=== Query Results ==="); while (rs.next()) { Object value = rs.getObject(1); String stringValue = rs.getString(1); System.out.println("Value (Object): " + value); System.out.println("Value (String): " + stringValue); System.out.println("Value class: " + (value != null ? value.getClass() : "null")); } } } catch (SQLException e) { System.err.println("SQL Error: " + e.getMessage()); e.printStackTrace(); throw new RuntimeException(e); } } catch (SQLException e) { System.err.println("Connection Error: " + e.getMessage()); e.printStackTrace(); throw new RuntimeException(e); } TimeZone.setDefault(TimeZone.getTimeZone("UTC")); System.out.printf("\nThe driver {%s} has been initialized.%n", Driver.class); } ``` --- ## Additional Notes to the Reviewer ~* **This PR is a DRAFT** — unit tests will be added before marking it ready.~ * Early feedback is welcome, especially regarding: * The chosen mapping strategy for interval types. * Whether exposing `SIGNED_TYPES` is acceptable. * Enum / type-parsing paths that may need to be refactored. * If there are other interval forms worth testing (e.g., `INTERVAL DAY`, `INTERVAL SECOND`, mixed units), please let me know. * I’m open to suggestions on where interval-related tests should live in the test suite. --------- Signed-off-by: Roman Dryndik <dryndikroman@gmail.com> Co-authored-by: Samikshya Chand <148681192+samikshya-db@users.noreply.github.com>
1 parent e88e562 commit f2b2453

File tree

5 files changed

+72
-3
lines changed

5 files changed

+72
-3
lines changed

NEXT_CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,7 @@
99

1010
### Fixed
1111

12+
- Fix driver crash when using `INTERVAL` types.
13+
1214
---
1315
*Note: When making changes, please add your change under the appropriate section with a brief description.*

src/main/java/com/databricks/jdbc/api/impl/DatabricksResultSetMetaData.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,8 @@ public DatabricksResultSetMetaData(
456456
} else if (columnTypeText.equalsIgnoreCase(VARIANT)) {
457457
columnTypeName = ColumnInfoTypeName.STRING;
458458
columnTypeText = VARIANT;
459+
} else if (columnTypeText.toUpperCase().startsWith(INTERVAL)) {
460+
columnTypeName = ColumnInfoTypeName.INTERVAL;
459461
} else {
460462
columnTypeName =
461463
ColumnInfoTypeName.valueOf(metadataResultSetBuilder.stripBaseTypeName(columnTypeText));
@@ -492,7 +494,6 @@ public DatabricksResultSetMetaData(
492494
this.totalRows = -1;
493495
this.columns = columnsBuilder.build();
494496
this.columnNameIndex = CaseInsensitiveImmutableMap.copyOf(columnNameToIndexMap);
495-
;
496497
}
497498

498499
@Override

src/main/java/com/databricks/jdbc/common/util/DatabricksTypeUtil.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ public class DatabricksTypeUtil {
5353
public static final String STRUCT = "STRUCT";
5454
public static final String VARIANT = "VARIANT";
5555
public static final String CHAR = "CHAR";
56-
public static final String INTERVAL = "INTERVAL";
5756
public static final String GEOMETRY = "GEOMETRY";
5857
public static final String GEOGRAPHY = "GEOGRAPHY";
58+
public static final String INTERVAL = "INTERVAL";
5959
public static final String GEOMETRY_CLASS_NAME = "com.databricks.jdbc.api.IGeometry";
6060
public static final String GEOGRAPHY_CLASS_NAME = "com.databricks.jdbc.api.IGeography";
6161
public static final String MEASURE = "measure";
@@ -114,6 +114,8 @@ public static ColumnInfoTypeName getColumnInfoType(String typeName) {
114114
return ColumnInfoTypeName.NULL;
115115
case DatabricksTypeUtil.MAP:
116116
return ColumnInfoTypeName.MAP;
117+
case DatabricksTypeUtil.INTERVAL:
118+
return ColumnInfoTypeName.INTERVAL;
117119
}
118120
return ColumnInfoTypeName.USER_DEFINED_TYPE;
119121
}

src/test/java/com/databricks/jdbc/api/impl/DatabricksResultSetMetaDataTest.java

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,70 @@ public void testDatabricksResultSetMetaDataInitialization_DescribeQuery() throws
199199
{"col_struct", "struct<col_int:int,col_string:string>", "STRUCT", Types.STRUCT, 255, 0},
200200
{"col_array", "array<int>", "ARRAY", Types.ARRAY, 255, 0},
201201
{"col_map", "map<string,string>", "MAP", Types.VARCHAR, 255, 0},
202-
{"col_variant", "variant", "VARIANT", Types.VARCHAR, 255, 0}
202+
{"col_variant", "variant", "VARIANT", Types.VARCHAR, 255, 0},
203+
{"col_interval", "interval", "INTERVAL", Types.VARCHAR, 255, 0},
204+
{"col_interval_second", "interval second", "INTERVAL SECOND", Types.VARCHAR, 255, 0},
205+
{"col_interval_minute", "interval minute", "INTERVAL MINUTE", Types.VARCHAR, 255, 0},
206+
{"col_interval_hour", "interval hour", "INTERVAL HOUR", Types.VARCHAR, 255, 0},
207+
{"col_interval_day", "interval day", "INTERVAL DAY", Types.VARCHAR, 255, 0},
208+
{"col_interval_month", "interval month", "INTERVAL MONTH", Types.VARCHAR, 255, 0},
209+
{"col_interval_year", "interval year", "INTERVAL YEAR", Types.VARCHAR, 255, 0},
210+
{
211+
"col_interval_year_to_month",
212+
"interval year to month",
213+
"INTERVAL YEAR TO MONTH",
214+
Types.VARCHAR,
215+
255,
216+
0
217+
},
218+
{
219+
"col_interval_day_to_hour",
220+
"interval day to hour",
221+
"INTERVAL DAY TO HOUR",
222+
Types.VARCHAR,
223+
255,
224+
0
225+
},
226+
{
227+
"col_interval_day_to_minute",
228+
"interval day to minute",
229+
"INTERVAL DAY TO MINUTE",
230+
Types.VARCHAR,
231+
255,
232+
0
233+
},
234+
{
235+
"col_interval_day_to_second",
236+
"interval day to second",
237+
"INTERVAL DAY TO SECOND",
238+
Types.VARCHAR,
239+
255,
240+
0
241+
},
242+
{
243+
"col_interval_hour_to_minute",
244+
"interval hour to minute",
245+
"INTERVAL HOUR TO MINUTE",
246+
Types.VARCHAR,
247+
255,
248+
0
249+
},
250+
{
251+
"col_interval_hour_to_second",
252+
"interval hour to second",
253+
"INTERVAL HOUR TO SECOND",
254+
Types.VARCHAR,
255+
255,
256+
0
257+
},
258+
{
259+
"col_interval_minute_to_second",
260+
"interval minute to second",
261+
"INTERVAL MINUTE TO SECOND",
262+
Types.VARCHAR,
263+
255,
264+
0
265+
}
203266
};
204267

205268
List<String> columnNames =

src/test/java/com/databricks/jdbc/common/util/DatabricksTypeUtilTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ void testInferDatabricksType() {
284284
"NULL, NULL",
285285
"MAP, MAP",
286286
"CHAR, STRING",
287+
"INTERVAL, INTERVAL",
287288
"UNKNOWN, USER_DEFINED_TYPE"
288289
})
289290
public void testGetColumnInfoType(String inputTypeName, String expectedTypeName) {

0 commit comments

Comments
 (0)