Skip to content

Commit 91a395e

Browse files
authored
Merge pull request #368 from youngsofun/ts
feat: support getObject(int, LocalDate.class)
2 parents 41888e0 + 4b16b16 commit 91a395e

4 files changed

Lines changed: 74 additions & 75 deletions

File tree

databend-jdbc/pom.xml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,6 @@
8181
<groupId>com.squareup.okhttp3</groupId>
8282
<artifactId>okhttp-urlconnection</artifactId>
8383
</dependency>
84-
<dependency>
85-
<groupId>joda-time</groupId>
86-
<artifactId>joda-time</artifactId>
87-
</dependency>
8884
<dependency>
8985
<groupId>de.siegmar</groupId>
9086
<artifactId>fastcsv</artifactId>

databend-jdbc/src/main/java/com/databend/jdbc/AbstractDatabendResultSet.java

Lines changed: 39 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,13 @@
22

33
import com.databend.client.QueryResults;
44
import com.databend.client.QueryRowField;
5-
import com.databend.client.data.ColumnTypeHandler;
6-
import com.databend.client.data.ColumnTypeHandlerFactory;
7-
import com.databend.client.data.DatabendRawType;
85
import com.databend.client.errors.QueryErrors;
96
import com.databend.jdbc.annotation.NotImplemented;
107
import com.databend.jdbc.exception.DatabendUnsupportedOperationException;
118
import com.databend.jdbc.exception.DatabendWithQueryIdSqlException;
129
import com.google.common.collect.ImmutableList;
1310
import com.google.common.collect.ImmutableMap;
1411
import com.google.common.collect.Maps;
15-
import org.joda.time.DateTimeZone;
16-
import org.joda.time.LocalDate;
17-
import org.joda.time.format.DateTimeFormatter;
18-
import org.joda.time.format.ISODateTimeFormat;
1912

2013
import java.io.ByteArrayInputStream;
2114
import java.io.InputStream;
@@ -41,11 +34,20 @@
4134
import java.sql.Statement;
4235
import java.sql.Time;
4336
import java.sql.Timestamp;
37+
import java.time.LocalDate;
4438
import java.time.LocalDateTime;
4539
import java.time.ZoneId;
4640
import java.time.ZoneOffset;
4741
import java.time.ZonedDateTime;
48-
import java.util.*;
42+
import java.time.format.DateTimeFormatter;
43+
import java.util.Calendar;
44+
import java.util.GregorianCalendar;
45+
import java.util.Iterator;
46+
import java.util.List;
47+
import java.util.Map;
48+
import java.util.Objects;
49+
import java.util.Optional;
50+
import java.util.TimeZone;
4951
import java.util.concurrent.atomic.AtomicBoolean;
5052
import java.util.concurrent.atomic.AtomicLong;
5153
import java.util.concurrent.atomic.AtomicReference;
@@ -58,13 +60,13 @@
5860
import static java.lang.String.format;
5961
import static java.util.Locale.ENGLISH;
6062
import static java.util.Objects.requireNonNull;
61-
import static org.joda.time.DateTimeConstants.SECONDS_PER_DAY;
6263

6364
abstract class AbstractDatabendResultSet implements ResultSet {
6465
protected AtomicLong lastRequestTime = new AtomicLong();
6566

66-
static final DateTimeFormatter DATE_FORMATTER = ISODateTimeFormat.date();
67+
static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE;
6768
private static final int MAX_DATETIME_PRECISION = 12;
69+
private static final long SECONDS_PER_DAY = 86_400L;
6870
private static final long[] POWERS_OF_TEN = {
6971
1L,
7072
10L,
@@ -99,7 +101,7 @@ abstract class AbstractDatabendResultSet implements ResultSet {
99101
private final Map<String, Integer> fieldMap;
100102
private final List<DatabendColumnInfo> databendColumnInfoList;
101103
private final ResultSetMetaData resultSetMetaData;
102-
private final DateTimeZone resultTimeZone;
104+
private final ZoneId resultTimeZone;
103105
private final boolean isResultTimeZoneFromServer;
104106

105107
private final String queryId;
@@ -110,12 +112,12 @@ abstract class AbstractDatabendResultSet implements ResultSet {
110112
this.databendColumnInfoList = getColumnInfo(schema);
111113
this.results = requireNonNull(results, "results is null");
112114
this.resultSetMetaData = new DatabendResultSetMetaData(databendColumnInfoList);
113-
DateTimeZone timeZone = DateTimeZone.forTimeZone(TimeZone.getDefault());
115+
ZoneId timeZone = TimeZone.getDefault().toZoneId();
114116
boolean timeZoneFromServer = false;
115117
if (resultSetting != null) {
116118
String tz = resultSetting.get("timezone");
117119
if (tz != null) {
118-
timeZone = DateTimeZone.forID(tz);
120+
timeZone = ZoneId.of(tz);
119121
timeZoneFromServer = true;
120122
}
121123
}
@@ -176,27 +178,11 @@ static SQLException resultsException(QueryResults results, String originalSQL) {
176178
return new SQLException(message, String.valueOf(error.getCode()));
177179
}
178180

179-
private static Date parseDate(String value, DateTimeZone localTimeZone) {
180-
if (localTimeZone == null) {
181-
return java.sql.Date.valueOf(value);
182-
}
183-
long millis = DATE_FORMATTER.withZone(localTimeZone).parseMillis(String.valueOf(value));
184-
if (millis >= START_OF_MODERN_ERA_SECONDS * MILLISECONDS_PER_SECOND) {
185-
return new Date(millis);
186-
}
187-
188-
// The chronology used by default by Joda is not historically accurate for dates
189-
// preceding the introduction of the Gregorian calendar and is not consistent with
190-
// java.sql.Date (the same millisecond value represents a different year/month/day)
191-
// before the 20th century. For such dates we are falling back to using the more
192-
// expensive GregorianCalendar; note that Joda also has a chronology that works for
193-
// older dates, but it uses a slightly different algorithm and yields results that
194-
// are not compatible with java.sql.Date.
195-
LocalDate localDate = DATE_FORMATTER.parseLocalDate(String.valueOf(value));
196-
Calendar calendar = new GregorianCalendar(localDate.getYear(), localDate.getMonthOfYear() - 1, localDate.getDayOfMonth());
197-
calendar.setTimeZone(TimeZone.getTimeZone(ZoneId.of(localTimeZone.getID())));
198-
199-
return new Date(calendar.getTimeInMillis());
181+
private static Date parseDate(String value, ZoneId localTimeZone) {
182+
LocalDate localDate = LocalDate.parse(String.valueOf(value), DATE_FORMATTER);
183+
ZoneId zone = localTimeZone == null ? ZoneId.systemDefault() : localTimeZone;
184+
long millis = localDate.atStartOfDay(zone).toInstant().toEpochMilli();
185+
return new Date(millis);
200186
}
201187

202188
private static long rescale(long value, int fromPrecision, int toPrecision) {
@@ -565,10 +551,10 @@ public byte[] getBytes(int columnIndex)
565551
@Override
566552
public Date getDate(int columnIndex)
567553
throws SQLException {
568-
return getDate(columnIndex, (DateTimeZone) null);
554+
return getDate(columnIndex, (ZoneId) null);
569555
}
570556

571-
private Date getDate(int columnIndex, DateTimeZone userTimeZone)
557+
private Date getDate(int columnIndex, ZoneId userTimeZone)
572558
throws SQLException {
573559
Object value = column(columnIndex);
574560
if (value == null) {
@@ -588,15 +574,15 @@ public Time getTime(int columnIndex)
588574
return getTime(columnIndex, resultTimeZone);
589575
}
590576

591-
private Time getTime(int columnIndex, DateTimeZone localTimeZone)
577+
private Time getTime(int columnIndex, ZoneId localTimeZone)
592578
throws SQLException {
593579
Object value = column(columnIndex);
594580
if (value == null) {
595581
return null;
596582
}
597583

598584
try {
599-
return parseTime((String) value, ZoneId.of(localTimeZone.getID()));
585+
return parseTime((String) value, localTimeZone);
600586
} catch (IllegalArgumentException e) {
601587
throw new SQLException("Invalid time from server: " + value, e);
602588
}
@@ -608,19 +594,16 @@ public Timestamp getTimestamp(int columnIndex)
608594
return getTimestamp(columnIndex, resultTimeZone);
609595
}
610596

611-
private Timestamp getTimestamp(int columnIndex, DateTimeZone localTimeZone)
597+
private Timestamp getTimestamp(int columnIndex, ZoneId localTimeZone)
612598
throws SQLException {
613599
Object value = column(columnIndex);
614600

615601
if (value == null || "null".equalsIgnoreCase(value.toString())) {
616602
return null;
617603
}
618604

619-
if (localTimeZone == null || localTimeZone.getID() == null) {
620-
return parseTimestampAsSqlTimestamp((String) value, ZoneId.systemDefault());
621-
}
622-
623-
return parseTimestampAsSqlTimestamp((String) value, ZoneId.of(localTimeZone.getID()));
605+
ZoneId zone = localTimeZone == null ? ZoneId.systemDefault() : localTimeZone;
606+
return parseTimestampAsSqlTimestamp((String) value, zone);
624607
}
625608

626609
@Override
@@ -1309,6 +1292,7 @@ public Statement getStatement()
13091292
throw new SQLException("Statement not available");
13101293
}
13111294

1295+
// it’s legacy, rarely supported
13121296
@Override
13131297
public Object getObject(int columnIndex, Map<String, Class<?>> map)
13141298
throws SQLException {
@@ -1373,8 +1357,7 @@ public Array getArray(String columnLabel)
13731357
@Override
13741358
public Date getDate(int columnIndex, Calendar cal)
13751359
throws SQLException {
1376-
// cal into joda local timezone
1377-
DateTimeZone timeZone = DateTimeZone.forTimeZone(cal.getTimeZone());
1360+
ZoneId timeZone = cal == null ? null : cal.getTimeZone().toZoneId();
13781361
return getDate(columnIndex, timeZone);
13791362
}
13801363

@@ -1387,8 +1370,7 @@ public Date getDate(String columnLabel, Calendar cal)
13871370
@Override
13881371
public Time getTime(int columnIndex, Calendar cal)
13891372
throws SQLException {
1390-
// cal into joda local timezone
1391-
DateTimeZone timeZone = DateTimeZone.forTimeZone(cal.getTimeZone());
1373+
ZoneId timeZone = cal == null ? null : cal.getTimeZone().toZoneId();
13921374
return getTime(columnIndex, timeZone);
13931375
}
13941376

@@ -1405,8 +1387,7 @@ public Timestamp getTimestamp(int columnIndex, Calendar cal)
14051387
if (isResultTimeZoneFromServer) {
14061388
return getTimestamp(columnIndex, resultTimeZone);
14071389
}
1408-
// cal into joda local timezone
1409-
DateTimeZone timeZone = DateTimeZone.forTimeZone(cal.getTimeZone());
1390+
ZoneId timeZone = cal == null ? null : cal.getTimeZone().toZoneId();
14101391
return getTimestamp(columnIndex, timeZone);
14111392
}
14121393

@@ -1772,15 +1753,17 @@ public <T> T getObject(int columnIndex, Class<T> type)
17721753
if (type == null) {
17731754
throw new SQLException("type is null");
17741755
}
1775-
String columnTypeStr = this.resultSetMetaData.getColumnTypeName(columnIndex);
1776-
DatabendRawType databendRawType = new DatabendRawType(columnTypeStr);
1777-
ColumnTypeHandler columnTypeHandler = ColumnTypeHandlerFactory.getTypeHandler(databendRawType);
17781756

1779-
Object object = column(columnIndex);
1780-
if (object == null) {
1757+
Object value = column(columnIndex);
1758+
if (value == null) {
17811759
return null;
17821760
}
1783-
return (T) object;
1761+
1762+
if (type == java.time.LocalDate.class) {
1763+
value = LocalDate.parse((String) value);
1764+
}
1765+
1766+
return (T) value;
17841767
}
17851768

17861769
@Override

databend-jdbc/src/main/java/com/databend/jdbc/DatabendPreparedStatement.java

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@
55
import com.databend.jdbc.parser.BatchInsertUtils;
66
import com.fasterxml.jackson.core.JsonProcessingException;
77
import com.fasterxml.jackson.databind.ObjectMapper;
8-
import org.joda.time.format.DateTimeFormat;
9-
import org.joda.time.format.DateTimeFormatter;
10-
import org.joda.time.format.ISODateTimeFormat;
11-
128
import java.io.*;
139
import java.math.BigDecimal;
1410
import java.net.URL;
@@ -33,10 +29,12 @@
3329

3430
public class DatabendPreparedStatement extends DatabendStatement implements PreparedStatement {
3531
private static final Logger logger = Logger.getLogger(DatabendPreparedStatement.class.getPackage().getName());
36-
static final DateTimeFormatter DATE_FORMATTER = ISODateTimeFormat.date();
32+
static final java.time.format.DateTimeFormatter DATE_FORMATTER = ISO_LOCAL_DATE;
3733
private final RawStatementWrapper rawStatement;
38-
static final DateTimeFormatter TIME_FORMATTER = DateTimeFormat.forPattern("HH:mm:ss.SSS");
39-
static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSS");
34+
static final java.time.format.DateTimeFormatter TIME_FORMATTER = java.time.format.DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
35+
static final java.time.format.DateTimeFormatter TIMESTAMP_FORMATTER = java.time.format.DateTimeFormatter
36+
.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")
37+
.withZone(ZoneOffset.UTC);
4038
private final DatabendParameterMetaData paramMetaData;
4139
private static final java.time.format.DateTimeFormatter LOCAL_DATE_TIME_FORMATTER = new DateTimeFormatterBuilder()
4240
.append(ISO_LOCAL_DATE)
@@ -838,7 +836,9 @@ public void setNClob(int i, Reader reader)
838836
private String toDateLiteral(Object value) throws IllegalArgumentException {
839837
requireNonNull(value, "value is null");
840838
if (value instanceof java.util.Date) {
841-
return DATE_FORMATTER.print(((java.util.Date) value).getTime());
839+
Instant instant = ((java.util.Date) value).toInstant();
840+
LocalDate localDate = instant.atZone(ZoneId.systemDefault()).toLocalDate();
841+
return DATE_FORMATTER.format(localDate);
842842
}
843843
if (value instanceof LocalDate) {
844844
return ISO_LOCAL_DATE.format(((LocalDate) value));
@@ -856,7 +856,9 @@ private String toDateLiteral(Object value) throws IllegalArgumentException {
856856
private String toTimeLiteral(Object value)
857857
throws IllegalArgumentException {
858858
if (value instanceof java.util.Date) {
859-
return TIME_FORMATTER.print(((java.util.Date) value).getTime());
859+
Instant instant = ((java.util.Date) value).toInstant();
860+
LocalTime localTime = instant.atZone(ZoneId.systemDefault()).toLocalTime();
861+
return TIME_FORMATTER.format(localTime);
860862
}
861863
if (value instanceof LocalTime) {
862864
return ISO_LOCAL_TIME.format((LocalTime) value);
@@ -874,7 +876,8 @@ private String toTimeLiteral(Object value)
874876
private String toTimestampLiteral(Object value)
875877
throws IllegalArgumentException {
876878
if (value instanceof java.util.Date) {
877-
return TIMESTAMP_FORMATTER.print(((java.util.Date) value).getTime()) + "Z";
879+
// include java.sql.Timestamp
880+
return TIMESTAMP_FORMATTER.format(((java.util.Date) value).toInstant()) + "Z";
878881
}
879882
if (value instanceof LocalDateTime) {
880883
return LOCAL_DATE_TIME_FORMATTER.format(((LocalDateTime) value));

databend-jdbc/src/test/java/com/databend/jdbc/TestTypes.java

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,9 @@ public void testSetTimestamp(boolean sameTZ)
103103
statement.execute("set timezone='America/Los_Angeles'");
104104
}
105105

106-
Instant instant= Instant.parse("2021-07-12T14:30:55.123Z");
106+
System.out.println("get default" + TimeZone.getDefault());
107+
108+
Instant instant = Instant.parse("2021-07-12T14:30:55.123Z");
107109
Timestamp ts = Timestamp.from(instant);
108110
Assert.assertEquals(ts, Timestamp.valueOf("2021-07-12 22:30:55.123"));
109111

@@ -174,10 +176,6 @@ public Object[][] provideTimeZone() {
174176

175177
@Test(groups = "IT", dataProvider = "timezone")
176178
public void TestSetDate(String tz) throws SQLException {
177-
// if (Compatibility.skipServerBugLowerThen("1.2.844")) {
178-
// return;
179-
// }
180-
181179
try (Connection c = Utils.createConnection();
182180
Statement s = c.createStatement()) {
183181

@@ -208,4 +206,23 @@ public void TestSetDate(String tz) throws SQLException {
208206
Assert.assertFalse(r.next());
209207
}
210208
}
209+
@Test(groups = "IT")
210+
public void TestLocalDateObject() throws SQLException {
211+
if (Compatibility.skipDriverBugLowerThen( "0.4.3")) {
212+
return;
213+
}
214+
LocalDate expected = LocalDate.of(2020, 1, 10);
215+
try (Connection c = Utils.createConnection();
216+
Statement s = c.createStatement()) {
217+
s.execute("create or replace table t_local_date_object(a DATE)");
218+
try (PreparedStatement ps = c.prepareStatement("insert into t_local_date_object values (?)")) {
219+
ps.setObject(1, expected);
220+
Assert.assertEquals(ps.executeUpdate(), 1);
221+
}
222+
try (ResultSet r = s.executeQuery("select * from t_local_date_object")) {
223+
Assert.assertTrue(r.next());
224+
Assert.assertEquals(r.getObject(1, LocalDate.class), expected);
225+
}
226+
}
227+
}
211228
}

0 commit comments

Comments
 (0)