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.
This commit is contained in:
Marios Trivyzas 2019-03-27 17:13:18 +01:00
parent 707d40ce06
commit 8e049c5f58
No known key found for this signature in database
GPG Key ID: 8817B46B0CF36A3F
3 changed files with 47 additions and 55 deletions

View File

@ -9,6 +9,7 @@ package org.elasticsearch.xpack.sql.jdbc;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
@ -27,10 +28,9 @@ import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
*/
final class JdbcDateUtils {
private JdbcDateUtils() {
}
private JdbcDateUtils() {}
private static final long DAY_IN_MILLIS = 60 * 60 * 24 * 1000L;
private static final LocalDate EPOCH = LocalDate.of(1970, 1, 1);
static final DateTimeFormatter ISO_WITH_MILLIS = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
@ -58,20 +58,9 @@ final class JdbcDateUtils {
return new Date(zdt.toLocalDate().atStartOfDay(zdt.getZone()).toInstant().toEpochMilli());
}
/**
* In contrast to {@link JdbcDateUtils#asDate(String)} here we just want to eliminate
* the date part and just set it to EPOCH (1970-01-1)
*/
static Time asTime(long millisSinceEpoch) {
return new Time(utcMillisRemoveDate(millisSinceEpoch));
}
/**
* In contrast to {@link JdbcDateUtils#asDate(String)} here we just want to eliminate
* the date part and just set it to EPOCH (1970-01-1)
*/
static Time asTime(String date) {
return asTime(asMillisSinceEpoch(date));
ZonedDateTime zdt = asDateTime(date);
return new Time(zdt.toLocalTime().atDate(EPOCH).atZone(zdt.getZone()).toInstant().toEpochMilli());
}
static Timestamp asTimestamp(long millisSinceEpoch) {
@ -93,8 +82,4 @@ final class JdbcDateUtils {
return ctor.apply(((Number) value).longValue());
}
}
private static long utcMillisRemoveDate(long l) {
return l % DAY_IN_MILLIS;
}
}

View File

@ -23,10 +23,13 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Time;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
@ -37,15 +40,17 @@ import java.util.zip.ZipEntry;
import static org.elasticsearch.xpack.sql.action.BasicFormatter.FormatOption.CLI;
public abstract class JdbcTestUtils {
final class JdbcTestUtils {
public static final String SQL_TRACE = "org.elasticsearch.xpack.sql:TRACE";
private JdbcTestUtils() {}
public static final String JDBC_TIMEZONE = "timezone";
private static final int MAX_WIDTH = 20;
public static ZoneId UTC = ZoneId.of("Z");
static final String SQL_TRACE = "org.elasticsearch.xpack.sql:TRACE";
static final String JDBC_TIMEZONE = "timezone";
static final LocalDate EPOCH = LocalDate.of(1970, 1, 1);
public static void logResultSetMetadata(ResultSet rs, Logger logger) throws SQLException {
static void logResultSetMetadata(ResultSet rs, Logger logger) throws SQLException {
ResultSetMetaData metaData = rs.getMetaData();
// header
StringBuilder sb = new StringBuilder();
@ -75,35 +80,24 @@ public abstract class JdbcTestUtils {
logger.info(sb.toString());
}
private static final int MAX_WIDTH = 20;
public static void logResultSetData(ResultSet rs, Logger log) throws SQLException {
static void logResultSetData(ResultSet rs, Logger log) throws SQLException {
ResultSetMetaData metaData = rs.getMetaData();
StringBuilder sb = new StringBuilder();
StringBuilder column = new StringBuilder();
int columns = metaData.getColumnCount();
while (rs.next()) {
sb.setLength(0);
for (int i = 1; i <= columns; i++) {
column.setLength(0);
if (i > 1) {
sb.append(" | ");
}
sb.append(trimOrPad(column.append(rs.getString(i))));
}
log.info(sb);
log.info(rowAsString(rs, columns));
}
}
public static String resultSetCurrentData(ResultSet rs) throws SQLException {
static String resultSetCurrentData(ResultSet rs) throws SQLException {
ResultSetMetaData metaData = rs.getMetaData();
StringBuilder column = new StringBuilder();
int columns = metaData.getColumnCount();
return rowAsString(rs, metaData.getColumnCount());
}
private static String rowAsString(ResultSet rs, int columns) throws SQLException {
StringBuilder sb = new StringBuilder();
StringBuilder column = new StringBuilder();
for (int i = 1; i <= columns; i++) {
column.setLength(0);
if (i > 1) {
@ -153,7 +147,7 @@ public abstract class JdbcTestUtils {
logger.info("\n" + formatter.formatWithHeader(cols, data));
}
public static String of(long millis, String zoneId) {
static String of(long millis, String zoneId) {
return StringUtils.toString(ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of(zoneId)));
}
@ -165,7 +159,7 @@ public abstract class JdbcTestUtils {
* folders in the file-system (typically IDEs) or
* inside jars (gradle).
*/
public static List<URL> classpathResources(String pattern) throws Exception {
static List<URL> classpathResources(String pattern) throws Exception {
while (pattern.startsWith("/")) {
pattern = pattern.substring(1);
}
@ -234,4 +228,15 @@ public abstract class JdbcTestUtils {
}
return new Tuple<>(folder, file);
}
static Date asDate(long millis, ZoneId zoneId) {
return new java.sql.Date(
ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), zoneId)
.toLocalDate().atStartOfDay(zoneId).toInstant().toEpochMilli());
}
static Time asTime(long millis, ZoneId zoneId) {
return new Time(ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), zoneId)
.toLocalTime().atDate(JdbcTestUtils.EPOCH).atZone(zoneId).toInstant().toEpochMilli());
}
}

View File

@ -34,7 +34,6 @@ import java.sql.Timestamp;
import java.sql.Types;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
@ -61,6 +60,8 @@ import static java.util.Calendar.MONTH;
import static java.util.Calendar.SECOND;
import static java.util.Calendar.YEAR;
import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.JDBC_TIMEZONE;
import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.asDate;
import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.asTime;
import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.of;
public class ResultSetTestCase extends JdbcIntegrationTestCase {
@ -880,10 +881,7 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase {
doWithQuery(SELECT_ALL_FIELDS, (results) -> {
results.next();
ZoneId zoneId = ZoneId.of(timeZoneId);
java.sql.Date expectedDate = new java.sql.Date(
ZonedDateTime.ofInstant(Instant.ofEpochMilli(randomLongDate), zoneId)
.toLocalDate().atStartOfDay(zoneId).toInstant().toEpochMilli());
java.sql.Date expectedDate = asDate(randomLongDate, getZoneFromOffset(randomLongDate));
assertEquals(expectedDate, results.getDate("test_date"));
assertEquals(expectedDate, results.getDate(9));
@ -943,7 +941,7 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase {
doWithQuery(SELECT_ALL_FIELDS, (results) -> {
results.next();
java.sql.Time expectedTime = new java.sql.Time(randomLongDate % 86400000L);
java.sql.Time expectedTime = asTime(randomLongDate, getZoneFromOffset(randomLongDate));
assertEquals(expectedTime, results.getTime("test_date"));
assertEquals(expectedTime, results.getTime(9));
@ -1748,4 +1746,8 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase {
private String asDateString(long millis) {
return of(millis, timeZoneId);
}
private ZoneId getZoneFromOffset(Long randomLongDate) {
return ZoneId.of(ZoneId.of(timeZoneId).getRules().getOffset(Instant.ofEpochMilli(randomLongDate)).toString());
}
}