Skip to content

Commit 2221cc7

Browse files
authored
SQL: Fix getTime() methods in JDBC (#40484)
Previously, `getTime(colIdx/colLabel)` and `getObject(colIdx/colLabel, java.sql.Time.class)` methods were computing the time from a `ZonedDateTime` by applying day in millis modulo on the epoch millis of the `ZonedDateTime` object. This is wrong as we need to keep the time related fields at the timezone of the `ZonedDateTime` object and just set the date info to the epoch date (01/01/1970). Additionally fixes a testing issue as the original timezone id is converted to an offset string when parsing the response from the server.
1 parent c88222d commit 2221cc7

File tree

3 files changed

+47
-55
lines changed

3 files changed

+47
-55
lines changed

x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcDateUtils.java

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.sql.Date;
1010
import java.sql.Time;
1111
import java.sql.Timestamp;
12+
import java.time.LocalDate;
1213
import java.time.ZonedDateTime;
1314
import java.time.format.DateTimeFormatter;
1415
import java.time.format.DateTimeFormatterBuilder;
@@ -27,10 +28,9 @@
2728
*/
2829
final class JdbcDateUtils {
2930

30-
private JdbcDateUtils() {
31-
}
31+
private JdbcDateUtils() {}
3232

33-
private static final long DAY_IN_MILLIS = 60 * 60 * 24 * 1000L;
33+
private static final LocalDate EPOCH = LocalDate.of(1970, 1, 1);
3434

3535
static final DateTimeFormatter ISO_WITH_MILLIS = new DateTimeFormatterBuilder()
3636
.parseCaseInsensitive()
@@ -58,20 +58,9 @@ static Date asDate(String date) {
5858
return new Date(zdt.toLocalDate().atStartOfDay(zdt.getZone()).toInstant().toEpochMilli());
5959
}
6060

61-
/**
62-
* In contrast to {@link JdbcDateUtils#asDate(String)} here we just want to eliminate
63-
* the date part and just set it to EPOCH (1970-01-1)
64-
*/
65-
static Time asTime(long millisSinceEpoch) {
66-
return new Time(utcMillisRemoveDate(millisSinceEpoch));
67-
}
68-
69-
/**
70-
* In contrast to {@link JdbcDateUtils#asDate(String)} here we just want to eliminate
71-
* the date part and just set it to EPOCH (1970-01-1)
72-
*/
7361
static Time asTime(String date) {
74-
return asTime(asMillisSinceEpoch(date));
62+
ZonedDateTime zdt = asDateTime(date);
63+
return new Time(zdt.toLocalTime().atDate(EPOCH).atZone(zdt.getZone()).toInstant().toEpochMilli());
7564
}
7665

7766
static Timestamp asTimestamp(long millisSinceEpoch) {
@@ -93,8 +82,4 @@ static <R> R asDateTimeField(Object value, Function<String, R> asDateTimeMethod,
9382
return ctor.apply(((Number) value).longValue());
9483
}
9584
}
96-
97-
private static long utcMillisRemoveDate(long l) {
98-
return l % DAY_IN_MILLIS;
99-
}
10085
}

x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcTestUtils.java

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@
2323
import java.nio.file.Path;
2424
import java.nio.file.SimpleFileVisitor;
2525
import java.nio.file.attribute.BasicFileAttributes;
26+
import java.sql.Date;
2627
import java.sql.ResultSet;
2728
import java.sql.ResultSetMetaData;
2829
import java.sql.SQLException;
30+
import java.sql.Time;
2931
import java.time.Instant;
32+
import java.time.LocalDate;
3033
import java.time.ZoneId;
3134
import java.time.ZonedDateTime;
3235
import java.util.ArrayList;
@@ -37,15 +40,17 @@
3740

3841
import static org.elasticsearch.xpack.sql.action.BasicFormatter.FormatOption.CLI;
3942

40-
public abstract class JdbcTestUtils {
43+
final class JdbcTestUtils {
4144

42-
public static final String SQL_TRACE = "org.elasticsearch.xpack.sql:TRACE";
45+
private JdbcTestUtils() {}
4346

44-
public static final String JDBC_TIMEZONE = "timezone";
45-
46-
public static ZoneId UTC = ZoneId.of("Z");
47+
private static final int MAX_WIDTH = 20;
48+
49+
static final String SQL_TRACE = "org.elasticsearch.xpack.sql:TRACE";
50+
static final String JDBC_TIMEZONE = "timezone";
51+
static final LocalDate EPOCH = LocalDate.of(1970, 1, 1);
4752

48-
public static void logResultSetMetadata(ResultSet rs, Logger logger) throws SQLException {
53+
static void logResultSetMetadata(ResultSet rs, Logger logger) throws SQLException {
4954
ResultSetMetaData metaData = rs.getMetaData();
5055
// header
5156
StringBuilder sb = new StringBuilder();
@@ -75,35 +80,24 @@ public static void logResultSetMetadata(ResultSet rs, Logger logger) throws SQLE
7580
logger.info(sb.toString());
7681
}
7782

78-
private static final int MAX_WIDTH = 20;
79-
80-
public static void logResultSetData(ResultSet rs, Logger log) throws SQLException {
83+
static void logResultSetData(ResultSet rs, Logger log) throws SQLException {
8184
ResultSetMetaData metaData = rs.getMetaData();
82-
StringBuilder sb = new StringBuilder();
83-
StringBuilder column = new StringBuilder();
8485

8586
int columns = metaData.getColumnCount();
8687

8788
while (rs.next()) {
88-
sb.setLength(0);
89-
for (int i = 1; i <= columns; i++) {
90-
column.setLength(0);
91-
if (i > 1) {
92-
sb.append(" | ");
93-
}
94-
sb.append(trimOrPad(column.append(rs.getString(i))));
95-
}
96-
log.info(sb);
89+
log.info(rowAsString(rs, columns));
9790
}
9891
}
9992

100-
public static String resultSetCurrentData(ResultSet rs) throws SQLException {
93+
static String resultSetCurrentData(ResultSet rs) throws SQLException {
10194
ResultSetMetaData metaData = rs.getMetaData();
102-
StringBuilder column = new StringBuilder();
103-
104-
int columns = metaData.getColumnCount();
95+
return rowAsString(rs, metaData.getColumnCount());
96+
}
10597

98+
private static String rowAsString(ResultSet rs, int columns) throws SQLException {
10699
StringBuilder sb = new StringBuilder();
100+
StringBuilder column = new StringBuilder();
107101
for (int i = 1; i <= columns; i++) {
108102
column.setLength(0);
109103
if (i > 1) {
@@ -153,7 +147,7 @@ public static void logLikeCLI(ResultSet rs, Logger logger) throws SQLException {
153147
logger.info("\n" + formatter.formatWithHeader(cols, data));
154148
}
155149

156-
public static String of(long millis, String zoneId) {
150+
static String of(long millis, String zoneId) {
157151
return StringUtils.toString(ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of(zoneId)));
158152
}
159153

@@ -165,7 +159,7 @@ public static String of(long millis, String zoneId) {
165159
* folders in the file-system (typically IDEs) or
166160
* inside jars (gradle).
167161
*/
168-
public static List<URL> classpathResources(String pattern) throws Exception {
162+
static List<URL> classpathResources(String pattern) throws Exception {
169163
while (pattern.startsWith("/")) {
170164
pattern = pattern.substring(1);
171165
}
@@ -234,4 +228,15 @@ static Tuple<String, String> pathAndName(String string) {
234228
}
235229
return new Tuple<>(folder, file);
236230
}
237-
}
231+
232+
static Date asDate(long millis, ZoneId zoneId) {
233+
return new java.sql.Date(
234+
ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), zoneId)
235+
.toLocalDate().atStartOfDay(zoneId).toInstant().toEpochMilli());
236+
}
237+
238+
static Time asTime(long millis, ZoneId zoneId) {
239+
return new Time(ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), zoneId)
240+
.toLocalTime().atDate(JdbcTestUtils.EPOCH).atZone(zoneId).toInstant().toEpochMilli());
241+
}
242+
}

x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import java.sql.Types;
3535
import java.time.Instant;
3636
import java.time.ZoneId;
37-
import java.time.ZonedDateTime;
3837
import java.util.Arrays;
3938
import java.util.Calendar;
4039
import java.util.Date;
@@ -61,6 +60,8 @@
6160
import static java.util.Calendar.SECOND;
6261
import static java.util.Calendar.YEAR;
6362
import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.JDBC_TIMEZONE;
63+
import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.asDate;
64+
import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.asTime;
6465
import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.of;
6566

6667
public class ResultSetTestCase extends JdbcIntegrationTestCase {
@@ -880,10 +881,7 @@ public void testGettingDateWithoutCalendar() throws Exception {
880881
doWithQuery(SELECT_ALL_FIELDS, (results) -> {
881882
results.next();
882883

883-
ZoneId zoneId = ZoneId.of(timeZoneId);
884-
java.sql.Date expectedDate = new java.sql.Date(
885-
ZonedDateTime.ofInstant(Instant.ofEpochMilli(randomLongDate), zoneId)
886-
.toLocalDate().atStartOfDay(zoneId).toInstant().toEpochMilli());
884+
java.sql.Date expectedDate = asDate(randomLongDate, getZoneFromOffset(randomLongDate));
887885

888886
assertEquals(expectedDate, results.getDate("test_date"));
889887
assertEquals(expectedDate, results.getDate(9));
@@ -939,11 +937,11 @@ public void testGettingTimeWithoutCalendar() throws Exception {
939937
});
940938
Long randomLongDate = randomNonNegativeLong();
941939
indexSimpleDocumentWithTrueValues(randomLongDate);
942-
940+
943941
doWithQuery(SELECT_ALL_FIELDS, (results) -> {
944942
results.next();
945943

946-
java.sql.Time expectedTime = new java.sql.Time(randomLongDate % 86400000L);
944+
java.sql.Time expectedTime = asTime(randomLongDate, getZoneFromOffset(randomLongDate));
947945

948946
assertEquals(expectedTime, results.getTime("test_date"));
949947
assertEquals(expectedTime, results.getTime(9));
@@ -953,7 +951,7 @@ public void testGettingTimeWithoutCalendar() throws Exception {
953951
validateErrorsForTimeTestsWithoutCalendar(results::getTime);
954952
});
955953
}
956-
954+
957955
public void testGettingTimeWithCalendar() throws Exception {
958956
createIndex("test");
959957
updateMappingForNumericValuesTests("test");
@@ -1748,4 +1746,8 @@ private Connection esWithLeniency(boolean multiValueLeniency) throws SQLExceptio
17481746
private String asDateString(long millis) {
17491747
return of(millis, timeZoneId);
17501748
}
1749+
1750+
private ZoneId getZoneFromOffset(Long randomLongDate) {
1751+
return ZoneId.of(ZoneId.of(timeZoneId).getRules().getOffset(Instant.ofEpochMilli(randomLongDate)).toString());
1752+
}
17511753
}

0 commit comments

Comments
 (0)