Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes "ResultSet.getTime() returns null" bug #1

Merged
merged 5 commits into from
Jan 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 21 additions & 9 deletions src/main/java/org/opensearch/jdbc/ResultSetImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -347,14 +347,18 @@ private Date getDateX(int columnIndex, Calendar calendar) throws SQLException {
public Time getTime(int columnIndex) throws SQLException {
log.debug(() -> logEntry("getTime (%d)", columnIndex));
checkCursorOperationPossible();
Time value = getTimeX(columnIndex);
Time value = getTimeX(columnIndex, null);
log.debug(() -> logExit("getTime", value));
return value;
}

private Time getTimeX(int columnIndex) throws SQLException {
// TODO - add/check support
return getObjectX(columnIndex, Time.class);
private Time getTimeX(int columnIndex, Calendar calendar) throws SQLException {
Map<String, Object> conversionParams = null;
if (calendar != null) {
conversionParams = new HashMap<>();
conversionParams.put("calendar", calendar);
}
return getObjectX(columnIndex, Time.class, conversionParams);
}

@Override
Expand Down Expand Up @@ -494,7 +498,7 @@ public Date getDate(String columnLabel) throws SQLException {
public Time getTime(String columnLabel) throws SQLException {
log.debug(() -> logEntry("getTime (%s)", columnLabel));
checkCursorOperationPossible();
Time value = getTimeX(getColumnIndex(columnLabel));
Time value = getTimeX(getColumnIndex(columnLabel), null);
log.debug(() -> logExit("getTime", value));
return value;
}
Expand Down Expand Up @@ -1071,14 +1075,22 @@ public Date getDate(String columnLabel, Calendar cal) throws SQLException {

@Override
public Time getTime(int columnIndex, Calendar cal) throws SQLException {
// TODO - implement?
return null;
log.debug(() -> logEntry("getTime (%d, %s)", columnIndex,
cal == null ? "null" : "Calendar TZ= " + cal.getTimeZone()));
checkCursorOperationPossible();
Time value = getTimeX(columnIndex, cal);
log.debug(() -> logExit("getTime", value));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in b1f8e06 and b1de4b8

return value;
}

@Override
public Time getTime(String columnLabel, Calendar cal) throws SQLException {
// TODO - implement?
return null;
log.debug(() -> logEntry("getTime (%s, %s)", columnLabel,
cal == null ? "null" : "Calendar TZ= " + cal.getTimeZone()));
checkCursorOperationPossible();
Time value = getTimeX(getColumnIndex(columnLabel), cal);
log.debug(() -> logExit("getTime", value));
return value;
}

@Override
Expand Down
58 changes: 40 additions & 18 deletions src/main/java/org/opensearch/jdbc/types/TimeType.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@

import java.sql.SQLException;
import java.sql.Time;
import java.time.LocalTime;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Locale;
import java.util.Map;

public class TimeType implements TypeHelper<Time>{
Expand All @@ -24,41 +31,56 @@ public Time fromValue(Object value, Map<String, Object> conversionParams) throws
if (value == null) {
return null;
}
Calendar calendar = conversionParams != null ? (Calendar) conversionParams.get("calendar") : null;
if (value instanceof Time) {
return asTime((Time) value);
return (Time) value;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the returned value here be converted to localtime similar to the case where value is a string?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see what adding that extra step will accomplish in this case, so I return value as java.sql.Time when the value is of type java.sql.Time. In case of string, value needs to be checked and converted correctly, so LocalTime is used to do so.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gocha, thanks!

} else if (value instanceof String) {
return asTime((String) value);
return asTime((String) value, calendar);
} else if (value instanceof Number) {
return this.asTime((Number) value);
return asTime((Number) value);
} else {
throw objectConversionException(value);
}
}

public Time asTime(Time value) {
return localTimetoSqlTime(value.toLocalTime());
}
public Time asTime(String value, Calendar calendar) {
Time time;
LocalDateTime localDateTime;

public Time asTime(String value) throws SQLException {
return localTimetoSqlTime(toLocalTime(value));
}
try {
TemporalAccessor temporal = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
.parse(value);

localDateTime = LocalDateTime.from(temporal);
time = Time.valueOf(localDateTime.toLocalTime());
} catch (DateTimeParseException exception) {
time = Time.valueOf(value);
}

if (calendar == null) {
return time;
}

private Time localTimetoSqlTime(LocalTime localTime) {
return new Time(localTime.getHour(), localTime.getMinute(), localTime.getSecond());
localDateTime = time.toLocalTime().atDate(LocalDate.now());

return localDateTimeToTime(localDateTime, calendar);
}

public Time asTime(Number value) {
return new Time(value.longValue());
}

private LocalTime toLocalTime(String value) throws SQLException {
if (value == null)
throw stringConversionException(value, null);
return LocalTime.parse(value);
}

@Override
public String getTypeName() {
return "Time";
}

private Time localDateTimeToTime(LocalDateTime ldt, Calendar calendar) {
calendar.set(ldt.getYear(), ldt.getMonthValue()-1, ldt.getDayOfMonth(),
ldt.getHour(), ldt.getMinute(), ldt.getSecond());
calendar.set(Calendar.MILLISECOND, ldt.getNano()/1000000);

return new Time(new Timestamp(calendar.getTimeInMillis()).getTime());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public static class TimestampTypeConverter extends BaseTypeConverter {

private static final Set<Class> supportedJavaClasses = Collections.unmodifiableSet(
new HashSet<>(Arrays.asList(
String.class, Timestamp.class
String.class, Timestamp.class, Time.class
)));

private TimestampTypeConverter() {
Expand Down
60 changes: 59 additions & 1 deletion src/test/java/org/opensearch/jdbc/types/TimeTypeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@

import static org.junit.jupiter.api.Assertions.assertEquals;

import com.google.common.collect.ImmutableMap;
import org.junit.jupiter.api.Test;
import org.opensearch.jdbc.test.UTCTimeZoneTestExtension;
import java.sql.Time;
import java.time.LocalTime;
import java.util.Calendar;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
Expand All @@ -22,11 +27,64 @@ public class TimeTypeTest {
@CsvSource(value = {
"00:00:00, 00:00:00",
"01:01:01, 01:01:01",
"23:59:59, 23:59:59"
"23:59:59, 23:59:59",
"1880-12-22 00:00:00, 00:00:00",
"2000-01-10 01:01:01, 01:01:01",
"1998-08-17 23:59:59, 23:59:59"
})
void testTimeFromString(String inputString, String resultString) {
Time time = Assertions.assertDoesNotThrow(
() -> TimeType.INSTANCE.fromValue(inputString, null));
assertEquals(resultString, time.toString());
}

@ParameterizedTest
@CsvSource(value = {
"00:00:00, 00:00:00",
"01:01:01, 01:01:01",
"23:59:59, 23:59:59",
"1880-12-22 00:00:00, 00:00:00",
"2000-01-10 01:01:01, 01:01:01",
"1998-08-17 23:59:59, 23:59:59"
})
void testTimeFromStringWithCalendar(String inputString, String resultString) {
Time time = Assertions.assertDoesNotThrow(
() -> TimeType.INSTANCE.fromValue(
inputString, ImmutableMap.of("calendar", Calendar.getInstance())));
assertEquals(resultString, time.toString());
}

@Test
void testTimeFromTime() {
Time timeNow = Time.valueOf(LocalTime.now());
Time time = Assertions.assertDoesNotThrow(
() -> TimeType.INSTANCE.fromValue(timeNow, null));
assertEquals(timeNow, time);
}

@Test
void testTimeFromTimeWithCalendar() {
Time timeNow = Time.valueOf(LocalTime.now());
Time time = Assertions.assertDoesNotThrow(
() -> TimeType.INSTANCE.fromValue(
timeNow, ImmutableMap.of("calendar", Calendar.getInstance())));
assertEquals(timeNow, time);
}

@Test
void testTimeFromNumber() {
long currentTimestamp = System.currentTimeMillis();
Time time = Assertions.assertDoesNotThrow(
() -> TimeType.INSTANCE.fromValue(currentTimestamp, null));
assertEquals(currentTimestamp, time.getTime());
}

@Test
void testTimeFromNumberWithCalendar() {
long currentTimestamp = System.currentTimeMillis();
Time time = Assertions.assertDoesNotThrow(
() -> TimeType.INSTANCE.fromValue(
currentTimestamp, ImmutableMap.of("calendar", Calendar.getInstance())));
assertEquals(currentTimestamp, time.getTime());
}
}