[FEATURE] Lossless java.time support in getObject(col, Class) for DATE/TIMESTAMP (BC and pre-1582 values)
I maintainer di solito rispondono entro 1 giorno
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 25/100
Direzione di ricerca
Start by reviewing the open pull request #1781 and compare its changes with the requested JDBC 4.2 mappings for getObject(int/String, Class). The issue names no source files or tests; its failing examples cover DATE and TIMESTAMP values, including BC and pre-1582 dates. Done means the requested java.time mappings preserve Arrow source values, unsupported mappings raise SQLException, and existing getters keep their behavior.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Is your feature request related to a problem? Please describe.
Databricks SQL stores DATE and TIMESTAMP on the proleptic Gregorian calendar (Spark 3+), and the server sends exact epoch-days and epoch-microseconds over Arrow. The driver converts them to java.sql.Date / java.sql.Timestamp with Date.valueOf(LocalDate) / Timestamp.valueOf(LocalDateTime). Those legacy types use the hybrid Julian/Gregorian calendar and have no BC era, so values before 1582-10-15 cannot be returned exactly. This is the same behaviour as Spark's own toJavaTimestamp/toJavaDate, and we are not asking to change it.
The problem is that the driver currently has no lossless alternative:
getObject(col, Instant.class),getObject(col, LocalDateTime.class)andgetObject(col, OffsetDateTime.class)return ajava.lang.String(theconvertSqlTypeToSpecificJavaTypefallback). The caller gets aClassCastException.getObject(col, LocalDate.class)is derived fromjava.sql.Date.toLocalDate(), so it inherits the same loss.
Observed on 3.4.4 (also 3.1.1) against a SQL warehouse, with JVM and session in UTC and Arrow results (the default). Server values come from UNIX_MICROS / UNIX_DATE:
| Value | Server | Driver |
|---|---|---|
TO_TIMESTAMP('2016 bc', 'yyyy G') |
-125754422400000000 µs |
getTimestamp().toInstant() = -2016-12-15T00:00:00Z (−17 days); toString() = 2016-01-01 00:00:00.0 (era lost) |
TIMESTAMP '1000-01-01 00:00:00 UTC' |
-30610224000000000 µs |
toInstant() = 1000-01-06T00:00:00Z (+5 days) |
CAST(... 2016 bc ... AS DATE) |
-1455491 days |
getDate() = 2016-01-01 (AD); getObject(col, LocalDate.class) = 2016-01-01 |
DATE '1582-10-05' |
-141437 days |
getDate() = 1582-10-15; getObject(col, LocalDate.class) = 1582-10-15 |
getObject(ts, Instant/LocalDateTime/OffsetDateTime) |
n/a | returns String "2016-01-01 00:00:00.0" |
Applications that need exact historical values (for example, ingesting tables containing BC or pre-1582 dates) can't get them from the driver today.
Describe the solution you'd like
Support the JDBC 4.2 java.time mappings in getObject(int/String, Class), populated straight from the source value (Arrow vector / server string), without going through java.sql types:
| Column | LocalDate |
LocalDateTime |
Instant |
OffsetDateTime |
|---|---|---|---|---|
| DATE | exact | atStartOfDay() |
error | error |
| TIMESTAMP_NTZ | date part | exact | error (no zone) | error (no zone) |
| TIMESTAMP | session-local date | session-local fields (java.time zone rules, matching Databricks rendering) | exact | exact, UTC offset |
- Leave existing getters (
getDate,getTimestamp,getString,getObject(int)and theCalendaroverloads) unchanged, so there is no behaviour change for existing users. - Requests that can't be satisfied (for example,
Instantfrom TIMESTAMP_NTZ, or a temporal class on a non-temporal column) raise a clearSQLExceptioninstead of returning aString. - Arrow paths are exact. Text result paths are best effort: SEA
JSON_ARRAYresults already truncate TIMESTAMP to milliseconds and drop the BC era on the server (TO_TIMESTAMP('2016 bc','yyyy G')arrives as2016-01-01T00:00:00.000Z).
This is what pgjdbc, Trino, mssql-jdbc, MySQL Connector/J and Arrow Flight SQL JDBC do: legacy types stay field-based, and java.time is lossless. It also matches Spark's own lossless route (spark.sql.datetime.java8API.enabled).
I'm happy to contribute a PR. Proposed split:
- Read side: the
getObject(col, java.time.*)support above. - Write side, separately:
setObject(OffsetDateTime), which is currently unsupported.- Explicit ISO formatting with signed years in place of
toString(). - Binding
LocalDateTimeasTIMESTAMP_NTZ.
Describe alternatives or workarounds you've considered
- Make
java.sql.Timestampinstant-preserving (build it from epoch millis rather thanTimestamp.valueOf). Rejected:- It changes
toString()/getString()for every pre-1582 value;1000-01-01would print as0999-12-27. That breaks read →setTimestampround-trips. - It adds minute-level drift for pre-1900 values on non-UTC JVMs (
java.util.TimeZonehas no local mean time). - It still can't represent BC, year 0 or 1582-10-05..14.
- It diverges from Spark and the other drivers above.
- It changes
- Calendar-mode connection property. Same trade-offs, and it adds a mode to the driver.
- Rewriting queries to return
unix_micros(col)/unix_date(col)in the application. This works but changes result schemas, and needs per-query special handling for nulls and TIMESTAMP_NTZ.
Additional context
Failing test (each one reads a value back and checks it is unchanged). Connection and session are in UTC. The comment on each assertion is what 3.4.4 actually returns:
@Test void dateInGregorianGap() throws SQLException {
ResultSet rs = stmt.executeQuery("SELECT DATE '1582-10-05'"); rs.next();
assertEquals(LocalDate.of(1582, 10, 5), rs.getObject(1, LocalDate.class));
// actual: 1582-10-15 (moved 10 days forward)
}
@Test void bcDate() throws SQLException {
ResultSet rs = stmt.executeQuery("SELECT DATE '-2015-01-01'"); rs.next(); // 2016 BC
assertEquals(LocalDate.of(-2015, 1, 1), rs.getObject(1, LocalDate.class));
// actual: 2016-01-01 (BC turned into AD)
}
@Test void timestampBefore1582() throws SQLException {
ResultSet rs = stmt.executeQuery("SELECT TIMESTAMP '1000-01-01 00:00:00 UTC'"); rs.next();
assertEquals(Instant.parse("1000-01-01T00:00:00Z"), rs.getTimestamp(1).toInstant());
// actual: 1000-01-06T00:00:00Z (5 days late)
}
@Test void getObjectInstant() throws SQLException {
ResultSet rs = stmt.executeQuery("SELECT TIMESTAMP '1000-01-01 00:00:00 UTC'"); rs.next();
assertEquals(Instant.class, rs.getObject(1, Instant.class).getClass());
// actual: java.lang.String "1000-01-01 00:00:00.0" (not an Instant)
}
Environment: macOS, Java 17, Databricks JDBC 3.4.4 (also reproduced on 3.1.1), Databricks SQL warehouse (DBSQL 2026.44).
- Lingua principale
- Java
- Stelle
- 41
- Fork
- 49
- Merge medio
- 1g 7h
- PR unite (30g)
- 27
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di databricks/databricks-jdbc
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
databricks/databricks-jdbc#1782 ·
I maintainer di solito rispondono entro 1 giorno
-
[BUG] SEA metadata SHOW ... LIKE under-escapes backslashes; schema/table names containing '\' never matchForse già presa @gibkim l’ha presa 6 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
databricks/databricks-jdbc#1598 ·
I maintainer di solito rispondono entro 1 giorno
-
engineer-bot
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
databricks/databricks-jdbc#1568 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 3/5 1-2 giorni Idoneità per principianti 68/100
databricks/databricks-jdbc#1770 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
databricks/databricks-jdbc#1763 ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di databricks/databricks-jdbc
Issue simili
-
type: possible bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
-
面渣逆袭Spring事务传播机制勘误Aperta
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 80/100
itwanger/toBeBetterJavaer#218 ·
-
Markdown and Astro language servers do not start when the project path contains a non-ASCII character or a spaceForse già presa @RoiSoleil l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
eclipse-wildwebdeveloper/wildwebdeveloper#2277 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
johanhaleby/occurrent#1462 ·
I maintainer di solito rispondono entro 1 giorno
-
Enhancing the Issue TemplateAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
SasanLabs/VulnerableApp#912 ·
I maintainer di solito rispondono entro 1 giorno