diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcDateUtils.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcDateUtils.java index 8b1433780c7..b4210f2c44d 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcDateUtils.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcDateUtils.java @@ -58,12 +58,24 @@ 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 new Time(utcMillisRemoveDate(asMillisSinceEpoch(date))); + return asTime(asMillisSinceEpoch(date)); + } + + static Timestamp asTimestamp(long millisSinceEpoch) { + return new Timestamp(millisSinceEpoch); } static Timestamp asTimestamp(String date) { diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcResultSet.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcResultSet.java index 7d2329254b5..9b1fcb48901 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcResultSet.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcResultSet.java @@ -178,17 +178,17 @@ class JdbcResultSet implements ResultSet, JdbcWrapper { @Override public Date getDate(int columnIndex) throws SQLException { - return getDate(columnIndex, null); + return asDate(columnIndex); } @Override public Time getTime(int columnIndex) throws SQLException { - return getTime(columnIndex, null); + return asTime(columnIndex); } @Override public Timestamp getTimestamp(int columnIndex) throws SQLException { - return getTimestamp(columnIndex, null); + return asTimeStamp(columnIndex); } @Override @@ -244,7 +244,7 @@ class JdbcResultSet implements ResultSet, JdbcWrapper { return getDate(column(columnLabel)); } - private Long dateTime(int columnIndex) throws SQLException { + private Long dateTimeAsMillis(int columnIndex) throws SQLException { Object val = column(columnIndex); EsType type = columnType(columnIndex); try { @@ -270,13 +270,68 @@ class JdbcResultSet implements ResultSet, JdbcWrapper { } } + private Date asDate(int columnIndex) throws SQLException { + Object val = column(columnIndex); + + if (val == null) { + return null; + } + + try { + return JdbcDateUtils.asDate(val.toString()); + } catch (Exception e) { + EsType type = columnType(columnIndex); + throw new SQLException( + format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to a Date", val, type.getName()), e); + } + } + + private Time asTime(int columnIndex) throws SQLException { + Object val = column(columnIndex); + + if (val == null) { + return null; + } + + EsType type = columnType(columnIndex); + if (type == EsType.DATE) { + return new Time(0L); + } + + try { + return JdbcDateUtils.asTime(val.toString()); + } catch (Exception e) { + throw new SQLException( + format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to a Time", val, type.getName()), e); + } + } + + private Timestamp asTimeStamp(int columnIndex) throws SQLException { + Object val = column(columnIndex); + + if (val == null) { + return null; + } + + try { + if (val instanceof Number) { + return JdbcDateUtils.asTimestamp(((Number) val).longValue()); + } + return JdbcDateUtils.asTimestamp(val.toString()); + } catch (Exception e) { + EsType type = columnType(columnIndex); + throw new SQLException( + format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to a Timestamp", val, type.getName()), e); + } + } + private Calendar safeCalendar(Calendar calendar) { return calendar == null ? defaultCalendar : calendar; } @Override public Date getDate(int columnIndex, Calendar cal) throws SQLException { - return TypeConverter.convertDate(dateTime(columnIndex), safeCalendar(cal)); + return TypeConverter.convertDate(dateTimeAsMillis(columnIndex), safeCalendar(cal)); } @Override @@ -290,7 +345,7 @@ class JdbcResultSet implements ResultSet, JdbcWrapper { if (type == EsType.DATE) { return new Time(0L); } - return TypeConverter.convertTime(dateTime(columnIndex), safeCalendar(cal)); + return TypeConverter.convertTime(dateTimeAsMillis(columnIndex), safeCalendar(cal)); } @Override @@ -300,7 +355,7 @@ class JdbcResultSet implements ResultSet, JdbcWrapper { @Override public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { - return TypeConverter.convertTimestamp(dateTime(columnIndex), safeCalendar(cal)); + return TypeConverter.convertTimestamp(dateTimeAsMillis(columnIndex), safeCalendar(cal)); } @Override diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java index e0a64fa0ca1..f9bc90a093e 100644 --- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java +++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java @@ -32,6 +32,9 @@ import java.sql.SQLFeatureNotSupportedException; import java.sql.SQLType; 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; @@ -874,17 +877,13 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase { Long randomLongDate = randomNonNegativeLong(); indexSimpleDocumentWithTrueValues(randomLongDate); - Calendar connCalendar = Calendar.getInstance(TimeZone.getTimeZone(timeZoneId), Locale.ROOT); - doWithQuery(SELECT_ALL_FIELDS, (results) -> { results.next(); - connCalendar.setTimeInMillis(randomLongDate); - connCalendar.set(HOUR_OF_DAY, 0); - connCalendar.set(MINUTE, 0); - connCalendar.set(SECOND, 0); - connCalendar.set(MILLISECOND, 0); - java.sql.Date expectedDate = new java.sql.Date(connCalendar.getTimeInMillis()); + ZoneId zoneId = ZoneId.of(timeZoneId); + java.sql.Date expectedDate = new java.sql.Date( + ZonedDateTime.ofInstant(Instant.ofEpochMilli(randomLongDate), zoneId) + .toLocalDate().atStartOfDay(zoneId).toInstant().toEpochMilli()); assertEquals(expectedDate, results.getDate("test_date")); assertEquals(expectedDate, results.getDate(9)); @@ -892,7 +891,7 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase { assertEquals(expectedDate, results.getObject(9, java.sql.Date.class)); // bulk validation for all fields which are not of type date - validateErrorsForDateTimeTestsWithoutCalendar(results::getDate); + validateErrorsForDateTestsWithoutCalendar(results::getDate); }); } @@ -941,24 +940,17 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase { Long randomLongDate = randomNonNegativeLong(); indexSimpleDocumentWithTrueValues(randomLongDate); - Calendar c = Calendar.getInstance(TimeZone.getTimeZone(timeZoneId), Locale.ROOT); - doWithQuery(SELECT_ALL_FIELDS, (results) -> { results.next(); - c.setTimeInMillis(randomLongDate); - c.set(ERA, GregorianCalendar.AD); - c.set(YEAR, 1970); - c.set(MONTH, 0); - c.set(DAY_OF_MONTH, 1); - - assertEquals(results.getTime("test_date"), new java.sql.Time(c.getTimeInMillis())); - assertEquals(results.getTime(9), new java.sql.Time(c.getTimeInMillis())); - assertEquals(results.getObject("test_date", java.sql.Time.class), - new java.sql.Time(randomLongDate % 86400000L)); - assertEquals(results.getObject(9, java.sql.Time.class), - new java.sql.Time(randomLongDate % 86400000L)); - - validateErrorsForDateTimeTestsWithoutCalendar(results::getTime); + + java.sql.Time expectedTime = new java.sql.Time(randomLongDate % 86400000L); + + assertEquals(expectedTime, results.getTime("test_date")); + assertEquals(expectedTime, results.getTime(9)); + assertEquals(expectedTime, results.getObject("test_date", java.sql.Time.class)); + assertEquals(expectedTime, results.getObject(9, java.sql.Time.class)); + + validateErrorsForTimeTestsWithoutCalendar(results::getTime); }); } @@ -1689,15 +1681,25 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase { assertThrowsUnsupportedAndExpectErrorMessage(r, "Writes not supported"); } - private void validateErrorsForDateTimeTestsWithoutCalendar(CheckedFunction method) { + private void validateErrorsForDateTestsWithoutCalendar(CheckedFunction method) { SQLException sqle; for (Entry, SQLType> field : dateTimeTestingFields.entrySet()) { sqle = expectThrows(SQLException.class, () -> method.apply(field.getKey().v1())); assertEquals( - format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to a Long", + format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to a Date", field.getKey().v2(), field.getValue()), sqle.getMessage()); } } + + private void validateErrorsForTimeTestsWithoutCalendar(CheckedFunction method) { + SQLException sqle; + for (Entry, SQLType> field : dateTimeTestingFields.entrySet()) { + sqle = expectThrows(SQLException.class, () -> method.apply(field.getKey().v1())); + assertEquals( + format(Locale.ROOT, "Unable to convert value [%.128s] of type [%s] to a Time", + field.getKey().v2(), field.getValue()), sqle.getMessage()); + } + } private void validateErrorsForDateTimeTestsWithCalendar(Calendar c, CheckedBiFunction method) { SQLException sqle;