Skip to content

Commit

Permalink
Added checks of invalid values of Decimal
Browse files Browse the repository at this point in the history
  • Loading branch information
alex268 committed Oct 2, 2024
1 parent 96ccf40 commit 14a7bbd
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 35 deletions.
1 change: 1 addition & 0 deletions jdbc/src/main/java/tech/ydb/jdbc/YdbConst.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public final class YdbConst {
public static final String UNABLE_TO_CONVERT = "Cannot cast [%s] with value [%s] to [%s]";
public static final String UNABLE_TO_CONVERT_AS_URL = "Cannot cast as URL: ";
public static final String UNABLE_TO_CAST_TO_CLASS = "Cannot cast [%s] to class [%s]";
public static final String UNABLE_TO_CAST_TO_DECIMAL = "Cannot cast to decimal type %s: [%s] is %s";

public static final String MISSING_VALUE_FOR_PARAMETER = "Missing value for parameter: ";
public static final String MISSING_REQUIRED_VALUE = "Missing required value for parameter: ";
Expand Down
28 changes: 1 addition & 27 deletions jdbc/src/main/java/tech/ydb/jdbc/common/MappingGetters.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,32 +30,6 @@
import tech.ydb.table.values.Type;
import tech.ydb.table.values.Value;

import static tech.ydb.table.values.PrimitiveType.Bool;
import static tech.ydb.table.values.PrimitiveType.Bytes;
import static tech.ydb.table.values.PrimitiveType.Date;
import static tech.ydb.table.values.PrimitiveType.Datetime;
import static tech.ydb.table.values.PrimitiveType.Double;
import static tech.ydb.table.values.PrimitiveType.Float;
import static tech.ydb.table.values.PrimitiveType.Int16;
import static tech.ydb.table.values.PrimitiveType.Int32;
import static tech.ydb.table.values.PrimitiveType.Int64;
import static tech.ydb.table.values.PrimitiveType.Int8;
import static tech.ydb.table.values.PrimitiveType.Interval;
import static tech.ydb.table.values.PrimitiveType.Json;
import static tech.ydb.table.values.PrimitiveType.JsonDocument;
import static tech.ydb.table.values.PrimitiveType.Text;
import static tech.ydb.table.values.PrimitiveType.Timestamp;
import static tech.ydb.table.values.PrimitiveType.TzDate;
import static tech.ydb.table.values.PrimitiveType.TzDatetime;
import static tech.ydb.table.values.PrimitiveType.TzTimestamp;
import static tech.ydb.table.values.PrimitiveType.Uint16;
import static tech.ydb.table.values.PrimitiveType.Uint32;
import static tech.ydb.table.values.PrimitiveType.Uint64;
import static tech.ydb.table.values.PrimitiveType.Uint8;
import static tech.ydb.table.values.PrimitiveType.Uuid;
import static tech.ydb.table.values.PrimitiveType.Yson;
import static tech.ydb.table.values.Type.Kind.PRIMITIVE;

public class MappingGetters {
private MappingGetters() { }

Expand Down Expand Up @@ -95,7 +69,7 @@ static Getters buildGetters(Type type) {
value -> value.getDecimal().toBigDecimal().floatValue(),
value -> value.getDecimal().toBigDecimal().doubleValue(),
castToBytesNotSupported(clazz),
PrimitiveReader::getDecimal,
value -> value.getDecimal().toBigDecimal(),
castToClassNotSupported(clazz),
castToInstantNotSupported(clazz),
castToNStringNotSupported(clazz),
Expand Down
29 changes: 21 additions & 8 deletions jdbc/src/main/java/tech/ydb/jdbc/common/MappingSetters.java
Original file line number Diff line number Diff line change
Expand Up @@ -466,23 +466,36 @@ private static PrimitiveValue castToTimestamp(PrimitiveType type, Object x) thro
throw castNotSupported(type, x);
}

private static DecimalValue validateValue(DecimalType type, DecimalValue value, Object x) throws SQLException {
if (value.isInf()) {
throw new SQLException(String.format(YdbConst.UNABLE_TO_CAST_TO_DECIMAL, type, toString(x), "Infinite"));
}
if (value.isNegativeInf()) {
throw new SQLException(String.format(YdbConst.UNABLE_TO_CAST_TO_DECIMAL, type, toString(x), "-Infinite"));
}
if (value.isNan()) {
throw new SQLException(String.format(YdbConst.UNABLE_TO_CAST_TO_DECIMAL, type, toString(x), "NaN"));
}
return value;
}

private static DecimalValue castToDecimalValue(DecimalType type, Object x) throws SQLException {
if (x instanceof DecimalValue) {
return (DecimalValue) x;
return validateValue(type, (DecimalValue) x, x);
} else if (x instanceof BigDecimal) {
return type.newValue((BigDecimal) x);
return validateValue(type, type.newValue((BigDecimal) x), x);
} else if (x instanceof BigInteger) {
return type.newValue((BigInteger) x);
return validateValue(type, type.newValue((BigInteger) x), x);
} else if (x instanceof Long) {
return type.newValue((Long) x);
return validateValue(type, type.newValue((Long) x), x);
} else if (x instanceof Integer) {
return type.newValue((Integer) x);
return validateValue(type, type.newValue((Integer) x), x);
} else if (x instanceof Short) {
return type.newValue((Short) x);
return validateValue(type, type.newValue((Short) x), x);
} else if (x instanceof Byte) {
return type.newValue((Byte) x);
return validateValue(type, type.newValue((Byte) x), x);
} else if (x instanceof String) {
return type.newValue((String) x);
return validateValue(type, type.newValue((String) x), x);
}
throw castNotSupported(type.getKind(), x);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tech.ydb.jdbc.impl;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
Expand Down Expand Up @@ -982,4 +983,74 @@ private void assertNextDate(ResultSet rs, int key, LocalDate ld) throws SQLExcep
Assertions.assertEquals(ld.atStartOfDay(), rs.getObject("c_Date", LocalDateTime.class));
Assertions.assertEquals(ld.atStartOfDay(ZoneId.systemDefault()).toInstant(), rs.getObject("c_Date", Instant.class));
}

@ParameterizedTest(name = "with {0}")
@EnumSource(SqlQueries.JdbcQuery.class)
public void decimalTest(SqlQueries.JdbcQuery query) throws SQLException {
String upsert = TEST_TABLE.upsertOne(query, "c_Decimal", "Decimal(22, 9)");

// YDB partially ignores Decimal(22, 9) limit, but have hard limit to 35 digits
String maxValue = "9999999999" + "9999999999" + "9999999999" + "99999";
BigDecimal closeToInf = new BigDecimal(new BigInteger(maxValue), 9);
BigDecimal closeToNegInf = new BigDecimal(new BigInteger(maxValue).negate(), 9);
try (PreparedStatement ps = jdbc.connection().prepareStatement(upsert)) {
ps.setInt(1, 1);
ps.setBigDecimal(2, BigDecimal.valueOf(1.5d));
ps.execute();

ps.setInt(1, 2);
ps.setBigDecimal(2, BigDecimal.valueOf(-12345, 10)); // will be rounded to -0.000001234
ps.execute();

ps.setInt(1, 3);
ps.setBigDecimal(2, closeToInf);
ps.execute();

ps.setInt(1, 4);
ps.setBigDecimal(2, closeToNegInf);
ps.execute();

ps.setInt(1, 5);
ExceptionAssert.sqlException(""
+ "Cannot cast to decimal type Decimal(22, 9): "
+ "[class java.math.BigDecimal: 100000000000000000000000000.000000000] is Infinite",
() -> ps.setBigDecimal(2, closeToInf.add(BigDecimal.valueOf(1, 9)))
);

ExceptionAssert.sqlException(""
+ "Cannot cast to decimal type Decimal(22, 9): "
+ "[class java.math.BigDecimal: -100000000000000000000000000.000000000] is -Infinite",
() -> ps.setBigDecimal(2, closeToNegInf.subtract(BigDecimal.valueOf(1, 9)))
);

ExceptionAssert.sqlException(""
+ "Cannot cast to decimal type Decimal(22, 9): "
+ "[class java.math.BigDecimal: 100000000000000000000000000.000000001] is NaN",
() -> ps.setBigDecimal(2, closeToInf.add(BigDecimal.valueOf(2, 9)))
);
}

try (Statement st = jdbc.connection().createStatement()) {
try (ResultSet rs = st.executeQuery(TEST_TABLE.selectColumn("c_Decimal"))) {
assertNextDecimal(rs, 1, BigDecimal.valueOf(1.5d).setScale(9));
assertNextDecimal(rs, 2, BigDecimal.valueOf(-1234, 9));
assertNextDecimal(rs, 3, closeToInf);
assertNextDecimal(rs, 4, closeToNegInf);

Assertions.assertFalse(rs.next());
}
}
};

private void assertNextDecimal(ResultSet rs, int key, BigDecimal bg) throws SQLException {
Assertions.assertTrue(rs.next());
Assertions.assertEquals(key, rs.getInt("key"));

Object obj = rs.getObject("c_Decimal");
Assertions.assertTrue(obj instanceof BigDecimal);
Assertions.assertEquals(bg, obj);

BigDecimal decimal = rs.getBigDecimal("c_Decimal");
Assertions.assertEquals(bg, decimal);
}
}

0 comments on commit 14a7bbd

Please sign in to comment.