Skip to content

Commit

Permalink
Improve consistency of conversion error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
mrotteveel committed Oct 31, 2021
1 parent 4d0d4be commit fe72d56
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 144 deletions.
32 changes: 9 additions & 23 deletions src/main/org/firebirdsql/jdbc/field/AbstractWithTimeZoneField.java
Expand Up @@ -22,13 +22,10 @@
import org.firebirdsql.gds.ng.tz.TimeZoneDatatypeCoder;

import java.sql.SQLException;
import java.sql.SQLNonTransientException;
import java.time.*;
import java.time.format.DateTimeParseException;
import java.util.Calendar;

import static org.firebirdsql.jdbc.JavaTypeNameConstants.*;

/**
* Superclass for {@link FBTimeTzField}, {@link FBTimestampTzField} to handle legacy date/time types and common behaviour.
*
Expand All @@ -47,53 +44,42 @@ abstract class AbstractWithTimeZoneField extends FBField {
.getTimeZoneCodecFor(fieldDescriptor);
}

@Override
final OffsetDateTime getOffsetDateTime() throws SQLException {
if (isNull()) return null;

return timeZoneCodec.decodeOffsetDateTime(getFieldData());
}

@Override
final void setOffsetDateTime(OffsetDateTime offsetDateTime) throws SQLException {
setFieldData(getTimeZoneCodec().encodeOffsetDateTime(offsetDateTime));
}

@Override
final OffsetTime getOffsetTime() throws SQLException {
if (isNull()) return null;

return timeZoneCodec.decodeOffsetTime(getFieldData());
}

@Override
final void setOffsetTime(OffsetTime offsetTime) throws SQLException {
setFieldData(timeZoneCodec.encodeOffsetTime(offsetTime));
}

@Override
final ZonedDateTime getZonedDateTime() throws SQLException {
if (isNull()) return null;

return timeZoneCodec.decodeZonedDateTime(getFieldData());
}

@Override
final void setZonedDateTime(ZonedDateTime zonedDateTime) throws SQLException {
setFieldData(timeZoneCodec.encodeZonedDateTime(zonedDateTime));
}

@SuppressWarnings("unchecked")
@Override
public <T> T getObject(Class<T> type) throws SQLException {
if (type == null) {
throw new SQLNonTransientException("getObject called with type null");
}
switch (type.getName()) {
case OFFSET_TIME_CLASS_NAME:
return (T) getOffsetTime();
case OFFSET_DATE_TIME_CLASS_NAME:
return (T) getOffsetDateTime();
case ZONED_DATE_TIME_CLASS_NAME:
return (T) getZonedDateTime();
}
return super.getObject(type);
}

@Override
public java.sql.Time getTime() throws SQLException {
OffsetDateTime offsetDateTime = getOffsetDateTime();
Expand Down Expand Up @@ -172,14 +158,14 @@ final ZoneId getDefaultZoneId() {
return defaultZoneId = ZoneId.systemDefault();
}

void setStringParse(String value) throws SQLException {
private void setStringParse(String value) throws SQLException {
// TODO Better way to do this?
// TODO More lenient parsing?
if (value.indexOf('T') != -1) {
OffsetDateTime offsetDateTime = OffsetDateTime.parse(value.trim());
OffsetDateTime offsetDateTime = OffsetDateTime.parse(value);
setOffsetDateTime(offsetDateTime);
} else {
OffsetTime offsetTime = OffsetTime.parse(value.trim());
OffsetTime offsetTime = OffsetTime.parse(value);
setOffsetTime(offsetTime);
}
}
Expand Down
15 changes: 3 additions & 12 deletions src/main/org/firebirdsql/jdbc/field/FBBigDecimalField.java
Expand Up @@ -181,16 +181,7 @@ public void setShort(short value) throws SQLException {
}

public void setString(String value) throws SQLException {
if (setWhenNull(value)) return;

String string = value.trim();
try {
setBigDecimal(new BigDecimal(string));
} catch (NumberFormatException nex) {
SQLException conversionException = invalidSetConversion(String.class, string);
conversionException.initCause(nex);
throw conversionException;
}
setBigDecimal(fromString(value, BigDecimal::new));
}

public void setBigDecimal(BigDecimal value) throws SQLException {
Expand Down Expand Up @@ -363,9 +354,9 @@ protected static FieldDataSize getFieldDataSize(FieldDescriptor fieldDescriptor)

SQLException bigDecimalConversionError(FieldDescriptor fieldDescriptor, BigDecimal value) {
String message = String.format(
"Unsupported set conversion requested for field \"%s\" at %d (JDBC type %s), "
"Unsupported set conversion requested for field %s at index %d (JDBC type %s), "
+ "source type: " + BIG_DECIMAL_CLASS_NAME + ", reason: value %f out of range",
fieldDescriptor.getOriginalName(), fieldDescriptor.getPosition() + 1,
fieldDescriptor.getFieldName(), fieldDescriptor.getPosition() + 1,
getJdbcTypeName(JdbcTypeConverter.toJdbcType(fieldDescriptor)), value);
return new TypeConversionException(message);
}
Expand Down
4 changes: 1 addition & 3 deletions src/main/org/firebirdsql/jdbc/field/FBDateField.java
Expand Up @@ -78,9 +78,7 @@ public String getString() throws SQLException {

@Override
public void setString(String value) throws SQLException {
if (setWhenNull(value)) return;

setDate(Date.valueOf(value));
setDate(fromString(value, Date::valueOf));
}

@Override
Expand Down
37 changes: 25 additions & 12 deletions src/main/org/firebirdsql/jdbc/field/FBField.java
Expand Up @@ -35,6 +35,7 @@
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Map;
import java.util.function.Function;

import static java.util.Objects.requireNonNull;
import static org.firebirdsql.jdbc.JavaTypeNameConstants.*;
Expand Down Expand Up @@ -848,48 +849,60 @@ protected boolean isInvertTimeZone() {
return props.isTimestampUsesLocalTimezone();
}

SQLException invalidGetConversion(Class<?> requestedType) {
final SQLException invalidGetConversion(Class<?> requestedType) {
return invalidGetConversion(requestedType.getName(), null);
}

SQLException invalidGetConversion(Class<?> requestedType, String reason) {
final SQLException invalidGetConversion(Class<?> requestedType, String reason) {
return invalidGetConversion(requestedType.getName(), reason);
}

SQLException invalidGetConversion(String requestedTypeName) {
final SQLException invalidGetConversion(String requestedTypeName) {
return invalidGetConversion(requestedTypeName, null);
}

SQLException invalidGetConversion(String requestedTypeName, String reason) {
final SQLException invalidGetConversion(String requestedTypeName, String reason) {
String message = String.format(
"Unsupported get conversion requested for field \"%s\" at %d (JDBC type %s), target type: %s",
getName(), fieldDescriptor.getPosition() + 1, getJdbcTypeName(), requestedTypeName);
"Unsupported get conversion requested for field %s at index %d (JDBC type %s), target type: %s",
getAlias(), fieldDescriptor.getPosition() + 1, getJdbcTypeName(), requestedTypeName);
if (reason != null) {
message = message + ", reason: " + reason;
}
return new TypeConversionException(message);
}

SQLException invalidSetConversion(Class<?> sourceType) {
final SQLException invalidSetConversion(Class<?> sourceType) {
return invalidSetConversion(sourceType.getName(), null);
}

SQLException invalidSetConversion(Class<?> sourceType, String reason) {
final SQLException invalidSetConversion(Class<?> sourceType, String reason) {
return invalidSetConversion(sourceType.getName(), reason);
}

SQLException invalidSetConversion(String sourceTypeName) {
final SQLException invalidSetConversion(String sourceTypeName) {
return invalidSetConversion(sourceTypeName, null);
}

SQLException invalidSetConversion(String sourceTypeName, String reason) {
final SQLException invalidSetConversion(String sourceTypeName, String reason) {
String message = String.format(
"Unsupported set conversion requested for field \"%s\" at %d (JDBC type %s), source type: %s",
getName(), fieldDescriptor.getPosition() + 1, getJdbcTypeName(), sourceTypeName);
"Unsupported set conversion requested for field %s at index %d (JDBC type %s), source type: %s",
getAlias(), fieldDescriptor.getPosition() + 1, getJdbcTypeName(), sourceTypeName);
if (reason != null) {
message = message + ", reason: " + reason;
}
return new TypeConversionException(message);
}

final <T> T fromString(String value, Function<String, T> converter) throws SQLException {
if (value == null) return null;
String string = value.trim();
try {
return converter.apply(value);
} catch (RuntimeException e) {
SQLException conversionException = invalidSetConversion(String.class, string);
conversionException.initCause(e);
throw conversionException;
}
}

}
107 changes: 32 additions & 75 deletions src/main/org/firebirdsql/jdbc/field/FBStringField.java
Expand Up @@ -30,10 +30,8 @@
import java.math.BigInteger;
import java.sql.*;
import java.time.*;
import java.time.format.DateTimeParseException;
import java.util.Calendar;

import static org.firebirdsql.jdbc.JavaTypeNameConstants.*;
import java.util.function.Function;

/**
* Describe class <code>FBStringField</code> here.
Expand Down Expand Up @@ -135,16 +133,7 @@ public long getLong() throws SQLException {

@Override
public BigDecimal getBigDecimal() throws SQLException {
if (isNull()) return null;

String string = getString().trim();
try {
return new BigDecimal(string);
} catch (NumberFormatException e) {
SQLException conversionException = invalidGetConversion(BigDecimal.class, string);
conversionException.initCause(e);
throw conversionException;
}
return getValueAs(BigDecimal.class, BigDecimal::new);
}

@Override
Expand Down Expand Up @@ -219,18 +208,12 @@ public Date getDate(Calendar cal) throws SQLException {

@Override
public Date getDate() throws SQLException {
if (isNull()) return null;
return Date.valueOf(getString().trim());
return getValueAs(Date.class, Date::valueOf);
}

@Override
LocalDate getLocalDate() throws SQLException {
String string = getString();
try {
return string != null ? LocalDate.parse(string.trim()) : null;
} catch (DateTimeParseException e) {
throw new SQLException("Unable to convert value '" + string + "' to type " + LOCAL_TIME_CLASS_NAME, e);
}
return getValueAs(LocalDate.class, LocalDate::parse);
}

@Override
Expand All @@ -241,18 +224,12 @@ public Time getTime(Calendar cal) throws SQLException {

@Override
public Time getTime() throws SQLException {
if (isNull()) return null;
return Time.valueOf(getString().trim());
return getValueAs(Time.class, Time::valueOf);
}

@Override
LocalTime getLocalTime() throws SQLException {
String string = getString();
try {
return string != null ? LocalTime.parse(string.trim()) : null;
} catch (DateTimeParseException e) {
throw new SQLException("Unable to convert value '" + string + "' to type " + LOCAL_DATE_CLASS_NAME, e);
}
return getValueAs(LocalTime.class, LocalTime::parse);
}

@Override
Expand All @@ -263,78 +240,46 @@ public Timestamp getTimestamp(Calendar cal) throws SQLException {

@Override
public Timestamp getTimestamp() throws SQLException {
String string = getString();
if (string == null) return null;
string = string.trim();
int tIdx = string.indexOf('T');
if (tIdx != -1) {
// possibly yyyy-MM-ddTHH:mm:ss[.f...]
string = string.substring(0, tIdx) + ' ' + string.substring(tIdx + 1);
}
return Timestamp.valueOf(string);
return getValueAs(Timestamp.class, string -> {
int tIdx = string.indexOf('T');
if (tIdx != -1) {
// possibly yyyy-MM-ddTHH:mm:ss[.f...]
string = string.substring(0, tIdx) + ' ' + string.substring(tIdx + 1);
}
return Timestamp.valueOf(string);
});
}

@Override
LocalDateTime getLocalDateTime() throws SQLException {
String originalString = getString();
if (originalString == null) return null;
try {
String string = originalString.trim();
return getValueAs(LocalDateTime.class, string -> {
int spaceIdx = string.indexOf(' ');
if (spaceIdx != -1) {
// possibly yyyy-MM-dd HH:mm:ss[.f...]
string = string.substring(0, spaceIdx) + 'T' + string.substring(spaceIdx + 1);
}
return LocalDateTime.parse(string);
} catch (DateTimeParseException e) {
throw new SQLException(
"Unable to convert value '" + originalString + "' to type " + LOCAL_DATE_TIME_CLASS_NAME, e);
}
});
}

@Override
OffsetTime getOffsetTime() throws SQLException {
String string = getString();
try {
return string != null ? OffsetTime.parse(string.trim()) : null;
} catch (DateTimeParseException e) {
throw new SQLException("Unable to convert value '" + string + "' to type " + OFFSET_TIME_CLASS_NAME, e);
}
return getValueAs(OffsetTime.class, OffsetTime::parse);
}

@Override
OffsetDateTime getOffsetDateTime() throws SQLException {
String string = getString();
try {
return string != null ? OffsetDateTime.parse(string.trim()) : null;
} catch (DateTimeParseException e) {
throw new SQLException(
"Unable to convert value '" + string + "' to type " + OFFSET_DATE_TIME_CLASS_NAME, e);
}
return getValueAs(OffsetDateTime.class, OffsetDateTime::parse);
}

@Override
ZonedDateTime getZonedDateTime() throws SQLException {
String string = getString();
try {
return string != null ? ZonedDateTime.parse(string.trim()) : null;
} catch (DateTimeParseException e) {
throw new SQLException("Unable to convert value '" + string + "' to type " + ZONED_DATE_TIME_CLASS_NAME, e);
}
return getValueAs(ZonedDateTime.class, ZonedDateTime::parse);
}

@Override
public BigInteger getBigInteger() throws SQLException {
if (isNull()) return null;

String string = getString().trim();
try {
return new BigInteger(string);
} catch (NumberFormatException e) {
SQLException conversionException = invalidGetConversion(BigInteger.class, string);
conversionException.initCause(e);
throw conversionException;
}
return getValueAs(BigInteger.class, BigInteger::new);
}

//--- setXXX methods
Expand Down Expand Up @@ -520,4 +465,16 @@ private void setAsString(Object value) throws SQLException {
if (setWhenNull(value)) return;
setString(value.toString());
}

private <T> T getValueAs(Class<T> type, Function<String, T> converter) throws SQLException {
if (isNull()) return null;
String string = getString().trim();
try {
return converter.apply(string);
} catch (RuntimeException e) {
SQLException conversionException = invalidGetConversion(type, string);
conversionException.initCause(e);
throw conversionException;
}
}
}

0 comments on commit fe72d56

Please sign in to comment.