* SQL: Add BigDecimal support to JDBC (#56015) * Introduce BigDecimal support to JDBC -- fetching This commit adds support for the getBigDecimal() methods. * Allow BigDecimal params in double range A prepared statement will now accept a BigDecimal parameter as a proxy for a double, if the conversion is lossless. (cherry picked from commit e9a873ad7f387682e3472110b1d7c0514bd347c9) * Fix compilation error Dimond notation with anonymous inner classes not avail in Java8.
This commit is contained in:
parent
f159fd8a20
commit
47250b14a4
|
@ -123,7 +123,13 @@ class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException {
|
public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException {
|
||||||
setObject(parameterIndex, x, Types.BIGINT);
|
// ES lacks proper BigDecimal support, so this function simply maps a BigDecimal to a double, while verifying that no definition
|
||||||
|
// is lost (i.e. the original value can be conveyed as a double).
|
||||||
|
// While long (i.e. BIGINT) has a larger scale (than double), double has the higher precision more appropriate for BigDecimal.
|
||||||
|
if (x.compareTo(BigDecimal.valueOf(x.doubleValue())) != 0) {
|
||||||
|
throw new SQLException("BigDecimal value [" + x + "] out of supported double's range.");
|
||||||
|
}
|
||||||
|
setDouble(parameterIndex, x.doubleValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.sql.jdbc;
|
package org.elasticsearch.xpack.sql.jdbc;
|
||||||
|
|
||||||
|
import org.elasticsearch.common.SuppressForbidden;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
@ -300,7 +302,6 @@ class JdbcResultSet implements ResultSet, JdbcWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
return JdbcDateUtils.asDate(val.toString());
|
return JdbcDateUtils.asDate(val.toString());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new SQLException(
|
throw new SQLException(
|
||||||
|
@ -525,7 +526,11 @@ class JdbcResultSet implements ResultSet, JdbcWrapper {
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException {
|
public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException {
|
||||||
throw new SQLFeatureNotSupportedException("BigDecimal not supported");
|
BigDecimal bd = getBigDecimal(columnIndex);
|
||||||
|
// The API doesn't allow for specifying a rounding behavior, although BigDecimals did have a way of controlling rounding, even
|
||||||
|
// before the API got deprecated => default to fail if scaling can't return an exactly equal value, since this behavior was
|
||||||
|
// expected by (old) callers as well.
|
||||||
|
return bd == null ? null : bd.setScale(scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -546,8 +551,9 @@ class JdbcResultSet implements ResultSet, JdbcWrapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
@SuppressForbidden(reason="implementing deprecated method")
|
||||||
public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException {
|
public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException {
|
||||||
throw new SQLFeatureNotSupportedException("BigDecimal not supported");
|
return getBigDecimal(column(columnLabel), scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -594,12 +600,12 @@ class JdbcResultSet implements ResultSet, JdbcWrapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
|
public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
|
||||||
throw new SQLFeatureNotSupportedException("BigDecimal not supported");
|
return convert(columnIndex, BigDecimal.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BigDecimal getBigDecimal(String columnLabel) throws SQLException {
|
public BigDecimal getBigDecimal(String columnLabel) throws SQLException {
|
||||||
throw new SQLFeatureNotSupportedException("BigDecimal not supported");
|
return getBigDecimal(column(columnLabel));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -10,6 +10,7 @@ import org.elasticsearch.geometry.utils.WellKnownText;
|
||||||
import org.elasticsearch.xpack.sql.proto.StringUtils;
|
import org.elasticsearch.xpack.sql.proto.StringUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.sql.Date;
|
import java.sql.Date;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.SQLFeatureNotSupportedException;
|
import java.sql.SQLFeatureNotSupportedException;
|
||||||
|
@ -178,6 +179,9 @@ final class TypeConverter {
|
||||||
if (type == byte[].class) {
|
if (type == byte[].class) {
|
||||||
return (T) asByteArray(val, columnType, typeString);
|
return (T) asByteArray(val, columnType, typeString);
|
||||||
}
|
}
|
||||||
|
if (type == BigDecimal.class) {
|
||||||
|
return (T) asBigDecimal(val, columnType, typeString);
|
||||||
|
}
|
||||||
//
|
//
|
||||||
// JDK 8 types
|
// JDK 8 types
|
||||||
//
|
//
|
||||||
|
@ -537,6 +541,36 @@ final class TypeConverter {
|
||||||
throw new SQLFeatureNotSupportedException();
|
throw new SQLFeatureNotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static BigDecimal asBigDecimal(Object val, EsType columnType, String typeString) throws SQLException {
|
||||||
|
switch (columnType) {
|
||||||
|
case BOOLEAN:
|
||||||
|
return (Boolean) val ? BigDecimal.ONE : BigDecimal.ZERO;
|
||||||
|
case BYTE:
|
||||||
|
case SHORT:
|
||||||
|
case INTEGER:
|
||||||
|
case LONG:
|
||||||
|
return BigDecimal.valueOf(((Number) val).longValue());
|
||||||
|
case FLOAT:
|
||||||
|
case HALF_FLOAT:
|
||||||
|
// floats are passed in as doubles here, so we need to dip into string to keep original float's (reduced) precision.
|
||||||
|
return new BigDecimal(String.valueOf(((Number) val).floatValue()));
|
||||||
|
case DOUBLE:
|
||||||
|
case SCALED_FLOAT:
|
||||||
|
return BigDecimal.valueOf(((Number) val).doubleValue());
|
||||||
|
case KEYWORD:
|
||||||
|
case TEXT:
|
||||||
|
case CONSTANT_KEYWORD:
|
||||||
|
try {
|
||||||
|
return new BigDecimal((String) val);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
return failConversion(val, columnType, typeString, BigDecimal.class, nfe);
|
||||||
|
}
|
||||||
|
// TODO: should we implement numeric - interval types conversions too; ever needed? ODBC does mandate it
|
||||||
|
// https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/converting-data-from-c-to-sql-data-types
|
||||||
|
}
|
||||||
|
return failConversion(val, columnType, typeString, BigDecimal.class);
|
||||||
|
}
|
||||||
|
|
||||||
private static LocalDate asLocalDate(Object val, EsType columnType, String typeString) throws SQLException {
|
private static LocalDate asLocalDate(Object val, EsType columnType, String typeString) throws SQLException {
|
||||||
throw new SQLFeatureNotSupportedException();
|
throw new SQLFeatureNotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.qa.jdbc;
|
||||||
|
|
||||||
import org.elasticsearch.common.collect.Tuple;
|
import org.elasticsearch.common.collect.Tuple;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.JDBCType;
|
import java.sql.JDBCType;
|
||||||
import java.sql.ParameterMetaData;
|
import java.sql.ParameterMetaData;
|
||||||
|
@ -16,16 +17,12 @@ import java.sql.ResultSetMetaData;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.SQLSyntaxErrorException;
|
import java.sql.SQLSyntaxErrorException;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.startsWith;
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
|
|
||||||
public class PreparedStatementTestCase extends JdbcIntegrationTestCase {
|
public class PreparedStatementTestCase extends JdbcIntegrationTestCase {
|
||||||
|
|
||||||
public void testSupportedTypes() throws Exception {
|
public void testSupportedTypes() throws Exception {
|
||||||
index("library", builder -> {
|
|
||||||
builder.field("name", "Don Quixote");
|
|
||||||
builder.field("page_count", 1072);
|
|
||||||
});
|
|
||||||
|
|
||||||
String stringVal = randomAlphaOfLength(randomIntBetween(0, 1000));
|
String stringVal = randomAlphaOfLength(randomIntBetween(0, 1000));
|
||||||
int intVal = randomInt();
|
int intVal = randomInt();
|
||||||
long longVal = randomLong();
|
long longVal = randomLong();
|
||||||
|
@ -34,10 +31,11 @@ public class PreparedStatementTestCase extends JdbcIntegrationTestCase {
|
||||||
boolean booleanVal = randomBoolean();
|
boolean booleanVal = randomBoolean();
|
||||||
byte byteVal = randomByte();
|
byte byteVal = randomByte();
|
||||||
short shortVal = randomShort();
|
short shortVal = randomShort();
|
||||||
|
BigDecimal bigDecimalVal = BigDecimal.valueOf(randomDouble());
|
||||||
|
|
||||||
try (Connection connection = esJdbc()) {
|
try (Connection connection = esJdbc()) {
|
||||||
try (PreparedStatement statement = connection.prepareStatement(
|
try (PreparedStatement statement = connection.prepareStatement(
|
||||||
"SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, name FROM library WHERE page_count=?")) {
|
"SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ?")) {
|
||||||
statement.setString(1, stringVal);
|
statement.setString(1, stringVal);
|
||||||
statement.setInt(2, intVal);
|
statement.setInt(2, intVal);
|
||||||
statement.setLong(3, longVal);
|
statement.setLong(3, longVal);
|
||||||
|
@ -47,7 +45,7 @@ public class PreparedStatementTestCase extends JdbcIntegrationTestCase {
|
||||||
statement.setBoolean(7, booleanVal);
|
statement.setBoolean(7, booleanVal);
|
||||||
statement.setByte(8, byteVal);
|
statement.setByte(8, byteVal);
|
||||||
statement.setShort(9, shortVal);
|
statement.setShort(9, shortVal);
|
||||||
statement.setInt(10, 1072);
|
statement.setBigDecimal(10, bigDecimalVal);
|
||||||
|
|
||||||
try (ResultSet results = statement.executeQuery()) {
|
try (ResultSet results = statement.executeQuery()) {
|
||||||
ResultSetMetaData resultSetMetaData = results.getMetaData();
|
ResultSetMetaData resultSetMetaData = results.getMetaData();
|
||||||
|
@ -67,13 +65,23 @@ public class PreparedStatementTestCase extends JdbcIntegrationTestCase {
|
||||||
assertEquals(booleanVal, results.getBoolean(7));
|
assertEquals(booleanVal, results.getBoolean(7));
|
||||||
assertEquals(byteVal, results.getByte(8));
|
assertEquals(byteVal, results.getByte(8));
|
||||||
assertEquals(shortVal, results.getShort(9));
|
assertEquals(shortVal, results.getShort(9));
|
||||||
assertEquals("Don Quixote", results.getString(10));
|
assertEquals(bigDecimalVal, results.getBigDecimal(10));
|
||||||
assertFalse(results.next());
|
assertFalse(results.next());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testOutOfRangeBigDecimal() throws Exception {
|
||||||
|
try (Connection connection = esJdbc()) {
|
||||||
|
try (PreparedStatement statement = connection.prepareStatement("SELECT ?")) {
|
||||||
|
BigDecimal tooLarge = BigDecimal.valueOf(Double.MAX_VALUE).add(BigDecimal.ONE);
|
||||||
|
SQLException ex = expectThrows(SQLException.class, () -> statement.setBigDecimal(1, tooLarge));
|
||||||
|
assertThat(ex.getMessage(), equalTo("BigDecimal value [" + tooLarge + "] out of supported double's range."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void testUnsupportedParameterUse() throws Exception {
|
public void testUnsupportedParameterUse() throws Exception {
|
||||||
index("library", builder -> {
|
index("library", builder -> {
|
||||||
builder.field("name", "Don Quixote");
|
builder.field("name", "Don Quixote");
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
package org.elasticsearch.xpack.sql.qa.jdbc;
|
package org.elasticsearch.xpack.sql.qa.jdbc;
|
||||||
|
|
||||||
import org.elasticsearch.client.Request;
|
import org.elasticsearch.client.Request;
|
||||||
|
import org.elasticsearch.common.CheckedBiConsumer;
|
||||||
import org.elasticsearch.common.CheckedBiFunction;
|
import org.elasticsearch.common.CheckedBiFunction;
|
||||||
import org.elasticsearch.common.CheckedConsumer;
|
import org.elasticsearch.common.CheckedConsumer;
|
||||||
import org.elasticsearch.common.CheckedFunction;
|
import org.elasticsearch.common.CheckedFunction;
|
||||||
|
@ -14,12 +15,14 @@ import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.collect.Tuple;
|
import org.elasticsearch.common.collect.Tuple;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||||
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.xpack.sql.jdbc.EsType;
|
import org.elasticsearch.xpack.sql.jdbc.EsType;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.sql.Blob;
|
import java.sql.Blob;
|
||||||
import java.sql.Clob;
|
import java.sql.Clob;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
|
@ -41,6 +44,7 @@ import java.util.Date;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
@ -153,12 +157,11 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase {
|
||||||
|
|
||||||
// Byte values testing
|
// Byte values testing
|
||||||
public void testGettingValidByteWithoutCasting() throws Exception {
|
public void testGettingValidByteWithoutCasting() throws Exception {
|
||||||
byte random1 = randomByte();
|
List<Byte> byteTestValues = createTestDataForNumericValueTests(ESTestCase::randomByte);
|
||||||
byte random2 = randomValueOtherThan(random1, () -> randomByte());
|
byte random1 = byteTestValues.get(0);
|
||||||
byte random3 = randomValueOtherThanMany(Arrays.asList(random1, random2)::contains, () -> randomByte());
|
byte random2 = byteTestValues.get(1);
|
||||||
|
byte random3 = byteTestValues.get(2);
|
||||||
createTestDataForByteValueTests(random1, random2, random3);
|
|
||||||
|
|
||||||
doWithQuery("SELECT test_byte, test_null_byte, test_keyword FROM test", (results) -> {
|
doWithQuery("SELECT test_byte, test_null_byte, test_keyword FROM test", (results) -> {
|
||||||
ResultSetMetaData resultSetMetaData = results.getMetaData();
|
ResultSetMetaData resultSetMetaData = results.getMetaData();
|
||||||
|
|
||||||
|
@ -282,12 +285,11 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase {
|
||||||
|
|
||||||
// Short values testing
|
// Short values testing
|
||||||
public void testGettingValidShortWithoutCasting() throws Exception {
|
public void testGettingValidShortWithoutCasting() throws Exception {
|
||||||
short random1 = randomShort();
|
List<Short> shortTestValues = createTestDataForNumericValueTests(ESTestCase::randomShort);
|
||||||
short random2 = randomValueOtherThan(random1, () -> randomShort());
|
short random1 = shortTestValues.get(0);
|
||||||
short random3 = randomValueOtherThanMany(Arrays.asList(random1, random2)::contains, () -> randomShort());
|
short random2 = shortTestValues.get(1);
|
||||||
|
short random3 = shortTestValues.get(2);
|
||||||
createTestDataForShortValueTests(random1, random2, random3);
|
|
||||||
|
|
||||||
doWithQuery("SELECT test_short, test_null_short, test_keyword FROM test", (results) -> {
|
doWithQuery("SELECT test_short, test_null_short, test_keyword FROM test", (results) -> {
|
||||||
ResultSetMetaData resultSetMetaData = results.getMetaData();
|
ResultSetMetaData resultSetMetaData = results.getMetaData();
|
||||||
|
|
||||||
|
@ -405,12 +407,11 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase {
|
||||||
|
|
||||||
// Integer values testing
|
// Integer values testing
|
||||||
public void testGettingValidIntegerWithoutCasting() throws Exception {
|
public void testGettingValidIntegerWithoutCasting() throws Exception {
|
||||||
int random1 = randomInt();
|
List<Integer> integerTestValues = createTestDataForNumericValueTests(ESTestCase::randomInt);
|
||||||
int random2 = randomValueOtherThan(random1, () -> randomInt());
|
int random1 = integerTestValues.get(0);
|
||||||
int random3 = randomValueOtherThanMany(Arrays.asList(random1, random2)::contains, () -> randomInt());
|
int random2 = integerTestValues.get(1);
|
||||||
|
int random3 = integerTestValues.get(2);
|
||||||
createTestDataForIntegerValueTests(random1, random2, random3);
|
|
||||||
|
|
||||||
doWithQuery("SELECT test_integer,test_null_integer,test_keyword FROM test", (results) -> {
|
doWithQuery("SELECT test_integer,test_null_integer,test_keyword FROM test", (results) -> {
|
||||||
ResultSetMetaData resultSetMetaData = results.getMetaData();
|
ResultSetMetaData resultSetMetaData = results.getMetaData();
|
||||||
|
|
||||||
|
@ -520,12 +521,11 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase {
|
||||||
|
|
||||||
// Long values testing
|
// Long values testing
|
||||||
public void testGettingValidLongWithoutCasting() throws Exception {
|
public void testGettingValidLongWithoutCasting() throws Exception {
|
||||||
long random1 = randomLong();
|
List<Long> longTestValues = createTestDataForNumericValueTests(ESTestCase::randomLong);
|
||||||
long random2 = randomValueOtherThan(random1, () -> randomLong());
|
long random1 = longTestValues.get(0);
|
||||||
long random3 = randomValueOtherThanMany(Arrays.asList(random1, random2)::contains, () -> randomLong());
|
long random2 = longTestValues.get(1);
|
||||||
|
long random3 = longTestValues.get(2);
|
||||||
createTestDataForLongValueTests(random1, random2, random3);
|
|
||||||
|
|
||||||
doWithQuery("SELECT test_long, test_null_long, test_keyword FROM test", (results) -> {
|
doWithQuery("SELECT test_long, test_null_long, test_keyword FROM test", (results) -> {
|
||||||
ResultSetMetaData resultSetMetaData = results.getMetaData();
|
ResultSetMetaData resultSetMetaData = results.getMetaData();
|
||||||
|
|
||||||
|
@ -622,12 +622,11 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase {
|
||||||
|
|
||||||
// Double values testing
|
// Double values testing
|
||||||
public void testGettingValidDoubleWithoutCasting() throws Exception {
|
public void testGettingValidDoubleWithoutCasting() throws Exception {
|
||||||
double random1 = randomDouble();
|
List<Double> doubleTestValues = createTestDataForNumericValueTests(ESTestCase::randomDouble);
|
||||||
double random2 = randomValueOtherThan(random1, () -> randomDouble());
|
double random1 = doubleTestValues.get(0);
|
||||||
double random3 = randomValueOtherThanMany(Arrays.asList(random1, random2)::contains, () -> randomDouble());
|
double random2 = doubleTestValues.get(1);
|
||||||
|
double random3 = doubleTestValues.get(2);
|
||||||
createTestDataForDoubleValueTests(random1, random2, random3);
|
|
||||||
|
|
||||||
doWithQuery("SELECT test_double, test_null_double, test_keyword FROM test", (results) -> {
|
doWithQuery("SELECT test_double, test_null_double, test_keyword FROM test", (results) -> {
|
||||||
ResultSetMetaData resultSetMetaData = results.getMetaData();
|
ResultSetMetaData resultSetMetaData = results.getMetaData();
|
||||||
|
|
||||||
|
@ -711,12 +710,11 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase {
|
||||||
|
|
||||||
// Float values testing
|
// Float values testing
|
||||||
public void testGettingValidFloatWithoutCasting() throws Exception {
|
public void testGettingValidFloatWithoutCasting() throws Exception {
|
||||||
float random1 = randomFloat();
|
List<Float> floatTestValues = createTestDataForNumericValueTests(ESTestCase::randomFloat);
|
||||||
float random2 = randomValueOtherThan(random1, () -> randomFloat());
|
float random1 = floatTestValues.get(0);
|
||||||
float random3 = randomValueOtherThanMany(Arrays.asList(random1, random2)::contains, () -> randomFloat());
|
float random2 = floatTestValues.get(1);
|
||||||
|
float random3 = floatTestValues.get(2);
|
||||||
createTestDataForFloatValueTests(random1, random2, random3);
|
|
||||||
|
|
||||||
doWithQuery("SELECT test_float, test_null_float, test_keyword FROM test", (results) -> {
|
doWithQuery("SELECT test_float, test_null_float, test_keyword FROM test", (results) -> {
|
||||||
ResultSetMetaData resultSetMetaData = results.getMetaData();
|
ResultSetMetaData resultSetMetaData = results.getMetaData();
|
||||||
|
|
||||||
|
@ -791,7 +789,188 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase {
|
||||||
sqle.getMessage());
|
sqle.getMessage());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// BigDecimal fetching testing
|
||||||
|
//
|
||||||
|
static final Map<Class<? extends Number>, Integer> JAVA_TO_SQL_NUMERIC_TYPES_MAP = new HashMap<Class<? extends Number>, Integer>() {{
|
||||||
|
put(Byte.class, Types.TINYINT);
|
||||||
|
put(Short.class, Types.SMALLINT);
|
||||||
|
put(Integer.class, Types.INTEGER);
|
||||||
|
put(Long.class, Types.BIGINT);
|
||||||
|
put(Float.class, Types.REAL);
|
||||||
|
put(Double.class, Types.DOUBLE);
|
||||||
|
// TODO: no half & scaled float testing
|
||||||
|
}};
|
||||||
|
|
||||||
|
private static <T extends Number> void validateBigDecimalWithoutCasting(ResultSet results, List<T> testValues)
|
||||||
|
throws SQLException {
|
||||||
|
|
||||||
|
ResultSetMetaData resultSetMetaData = results.getMetaData();
|
||||||
|
|
||||||
|
Class<? extends Number> clazz = testValues.get(0).getClass();
|
||||||
|
String primitiveName = clazz.getSimpleName().toLowerCase(Locale.ROOT);
|
||||||
|
|
||||||
|
BigDecimal testVal1 = new BigDecimal(testValues.get(0).toString());
|
||||||
|
BigDecimal testVal2 = new BigDecimal(testValues.get(1).toString());
|
||||||
|
BigDecimal testVal3 = new BigDecimal(testValues.get(2).toString());
|
||||||
|
|
||||||
|
assertEquals(3, resultSetMetaData.getColumnCount());
|
||||||
|
assertEquals(JAVA_TO_SQL_NUMERIC_TYPES_MAP.get(clazz).longValue(), resultSetMetaData.getColumnType(1));
|
||||||
|
assertEquals(JAVA_TO_SQL_NUMERIC_TYPES_MAP.get(clazz).longValue(), resultSetMetaData.getColumnType(2));
|
||||||
|
|
||||||
|
assertTrue(results.next());
|
||||||
|
|
||||||
|
assertEquals(testVal1, results.getBigDecimal(1));
|
||||||
|
assertEquals(testVal1, results.getBigDecimal("test_" + primitiveName));
|
||||||
|
assertEquals(testVal1, results.getObject("test_" + primitiveName, BigDecimal.class));
|
||||||
|
assertEquals(results.getObject(1).getClass(), clazz);
|
||||||
|
|
||||||
|
assertNull(results.getBigDecimal(2));
|
||||||
|
assertTrue(results.wasNull());
|
||||||
|
assertNull(results.getObject("test_null_" + primitiveName));
|
||||||
|
assertTrue(results.wasNull());
|
||||||
|
|
||||||
|
assertTrue(results.next());
|
||||||
|
|
||||||
|
assertEquals(testVal2, results.getBigDecimal(1));
|
||||||
|
assertEquals(testVal2, results.getBigDecimal("test_" + primitiveName));
|
||||||
|
assertEquals(results.getObject(1).getClass(), clazz);
|
||||||
|
assertEquals(testVal3, results.getBigDecimal("test_keyword"));
|
||||||
|
|
||||||
|
assertFalse(results.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGettingValidBigDecimalFromBooleanWithoutCasting() throws Exception {
|
||||||
|
createTestDataForBooleanValueTests();
|
||||||
|
|
||||||
|
doWithQuery("SELECT test_boolean, test_null_boolean, test_keyword FROM test", results -> {
|
||||||
|
ResultSetMetaData resultSetMetaData = results.getMetaData();
|
||||||
|
assertEquals(3, resultSetMetaData.getColumnCount());
|
||||||
|
assertEquals(Types.BOOLEAN, resultSetMetaData.getColumnType(1));
|
||||||
|
assertEquals(Types.BOOLEAN, resultSetMetaData.getColumnType(2));
|
||||||
|
|
||||||
|
assertTrue(results.next());
|
||||||
|
|
||||||
|
assertEquals(BigDecimal.ONE, results.getBigDecimal(1));
|
||||||
|
assertEquals(BigDecimal.ONE, results.getBigDecimal("test_boolean"));
|
||||||
|
assertEquals(BigDecimal.ONE, results.getObject(1, BigDecimal.class));
|
||||||
|
|
||||||
|
assertNull(results.getBigDecimal(2));
|
||||||
|
assertTrue(results.wasNull());
|
||||||
|
assertNull(results.getBigDecimal("test_null_boolean"));
|
||||||
|
assertTrue(results.wasNull());
|
||||||
|
|
||||||
|
assertEquals(BigDecimal.ONE, results.getBigDecimal(3));
|
||||||
|
assertEquals(BigDecimal.ONE, results.getBigDecimal("test_keyword"));
|
||||||
|
|
||||||
|
assertTrue(results.next());
|
||||||
|
|
||||||
|
assertEquals(BigDecimal.ZERO, results.getBigDecimal(1));
|
||||||
|
assertEquals(BigDecimal.ZERO, results.getBigDecimal("test_boolean"));
|
||||||
|
assertEquals(BigDecimal.ZERO, results.getObject(1, BigDecimal.class));
|
||||||
|
|
||||||
|
assertNull(results.getBigDecimal(2));
|
||||||
|
assertTrue(results.wasNull());
|
||||||
|
assertNull(results.getBigDecimal("test_null_boolean"));
|
||||||
|
assertTrue(results.wasNull());
|
||||||
|
|
||||||
|
assertEquals(BigDecimal.ZERO, results.getBigDecimal(3));
|
||||||
|
assertEquals(BigDecimal.ZERO, results.getBigDecimal("test_keyword"));
|
||||||
|
|
||||||
|
assertFalse(results.next());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public void testGettingValidBigDecimalFromByteWithoutCasting() throws Exception {
|
||||||
|
List<Byte> byteTestValues = createTestDataForNumericValueTests(ESTestCase::randomByte);
|
||||||
|
doWithQuery("SELECT test_byte, test_null_byte, test_keyword FROM test", byteTestValues,
|
||||||
|
ResultSetTestCase::validateBigDecimalWithoutCasting);
|
||||||
|
}
|
||||||
|
public void testGettingValidBigDecimalFromShortWithoutCasting() throws Exception {
|
||||||
|
List<Short> shortTestValues = createTestDataForNumericValueTests(ESTestCase::randomShort);
|
||||||
|
doWithQuery("SELECT test_short, test_null_short, test_keyword FROM test", shortTestValues,
|
||||||
|
ResultSetTestCase::validateBigDecimalWithoutCasting);
|
||||||
|
}
|
||||||
|
public void testGettingValidBigDecimalFromIntegerWithoutCasting() throws Exception {
|
||||||
|
List<Integer> integerTestValues = createTestDataForNumericValueTests(ESTestCase::randomInt);
|
||||||
|
doWithQuery("SELECT test_integer, test_null_integer, test_keyword FROM test", integerTestValues,
|
||||||
|
ResultSetTestCase::validateBigDecimalWithoutCasting);
|
||||||
|
}
|
||||||
|
public void testGettingValidBigDecimalFromLongWithoutCasting() throws Exception {
|
||||||
|
List<Long> longTestValues = createTestDataForNumericValueTests(ESTestCase::randomLong);
|
||||||
|
doWithQuery("SELECT test_long, test_null_long, test_keyword FROM test", longTestValues,
|
||||||
|
ResultSetTestCase::validateBigDecimalWithoutCasting);
|
||||||
|
}
|
||||||
|
public void testGettingValidBigDecimalFromFloatWithoutCasting() throws Exception {
|
||||||
|
List<Float> floatTestValues = createTestDataForNumericValueTests(ESTestCase::randomFloat);
|
||||||
|
doWithQuery("SELECT test_float, test_null_float, test_keyword FROM test", floatTestValues,
|
||||||
|
ResultSetTestCase::validateBigDecimalWithoutCasting);
|
||||||
|
}
|
||||||
|
public void testGettingValidBigDecimalFromDoubleWithoutCasting() throws Exception {
|
||||||
|
List<Double> doubleTestValues = createTestDataForNumericValueTests(ESTestCase::randomDouble);
|
||||||
|
doWithQuery("SELECT test_double, test_null_double, test_keyword FROM test", doubleTestValues,
|
||||||
|
ResultSetTestCase::validateBigDecimalWithoutCasting);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGettingValidBigDecimalWithCasting() throws Exception {
|
||||||
|
Map<String,Number> map = createTestDataForNumericValueTypes(() -> randomDouble());
|
||||||
|
|
||||||
|
doWithQuery(SELECT_WILDCARD, (results) -> {
|
||||||
|
results.next();
|
||||||
|
for (Entry<String, Number> e : map.entrySet()) {
|
||||||
|
BigDecimal actualByObj = results.getObject(e.getKey(), BigDecimal.class);
|
||||||
|
BigDecimal actualByType = results.getBigDecimal(e.getKey());
|
||||||
|
|
||||||
|
BigDecimal expected;
|
||||||
|
if (e.getValue() instanceof Double) {
|
||||||
|
expected = BigDecimal.valueOf(e.getValue().doubleValue());
|
||||||
|
} else if (e.getValue() instanceof Float) {
|
||||||
|
expected = new BigDecimal(String.valueOf(e.getValue().floatValue()));
|
||||||
|
} else {
|
||||||
|
expected = BigDecimal.valueOf(e.getValue().longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals("For field " + e.getKey(), expected, actualByObj);
|
||||||
|
assertEquals("For field " + e.getKey(), expected, actualByType);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGettingInvalidBigDecimal() throws Exception {
|
||||||
|
createIndex("test");
|
||||||
|
updateMappingForNumericValuesTests("test");
|
||||||
|
updateMapping("test", builder -> {
|
||||||
|
builder.startObject("test_keyword").field("type", "keyword").endObject();
|
||||||
|
builder.startObject("test_date").field("type", "date").endObject();
|
||||||
|
});
|
||||||
|
|
||||||
|
String randomString = randomUnicodeOfCodepointLengthBetween(128, 256);
|
||||||
|
long randomDate = randomNonNegativeLong();
|
||||||
|
|
||||||
|
index("test", "1", builder -> {
|
||||||
|
builder.field("test_keyword", randomString);
|
||||||
|
builder.field("test_date", randomDate);
|
||||||
|
});
|
||||||
|
|
||||||
|
doWithQuery(SELECT_WILDCARD, (results) -> {
|
||||||
|
results.next();
|
||||||
|
|
||||||
|
SQLException sqle = expectThrows(SQLException.class, () -> results.getBigDecimal("test_keyword"));
|
||||||
|
assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [KEYWORD] to [BigDecimal]", randomString),
|
||||||
|
sqle.getMessage());
|
||||||
|
sqle = expectThrows(SQLException.class, () -> results.getObject("test_keyword", BigDecimal.class));
|
||||||
|
assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [KEYWORD] to [BigDecimal]", randomString),
|
||||||
|
sqle.getMessage());
|
||||||
|
|
||||||
|
sqle = expectThrows(SQLException.class, () -> results.getBigDecimal("test_date"));
|
||||||
|
assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [DATETIME] to [BigDecimal]",
|
||||||
|
asDateString(randomDate)), sqle.getMessage());
|
||||||
|
sqle = expectThrows(SQLException.class, () -> results.getObject("test_date", BigDecimal.class));
|
||||||
|
assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [DATETIME] to [BigDecimal]",
|
||||||
|
asDateString(randomDate)), sqle.getMessage());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void testGettingBooleanValues() throws Exception {
|
public void testGettingBooleanValues() throws Exception {
|
||||||
createIndex("test");
|
createIndex("test");
|
||||||
updateMappingForNumericValuesTests("test");
|
updateMappingForNumericValuesTests("test");
|
||||||
|
@ -1361,8 +1540,6 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase {
|
||||||
assertThrowsUnsupportedAndExpectErrorMessage(() -> r.getAsciiStream(1), "AsciiStream not supported");
|
assertThrowsUnsupportedAndExpectErrorMessage(() -> r.getAsciiStream(1), "AsciiStream not supported");
|
||||||
assertThrowsUnsupportedAndExpectErrorMessage(() -> r.getArray("test"), "Array not supported");
|
assertThrowsUnsupportedAndExpectErrorMessage(() -> r.getArray("test"), "Array not supported");
|
||||||
assertThrowsUnsupportedAndExpectErrorMessage(() -> r.getArray(1), "Array not supported");
|
assertThrowsUnsupportedAndExpectErrorMessage(() -> r.getArray(1), "Array not supported");
|
||||||
assertThrowsUnsupportedAndExpectErrorMessage(() -> r.getBigDecimal("test"), "BigDecimal not supported");
|
|
||||||
assertThrowsUnsupportedAndExpectErrorMessage(() -> r.getBigDecimal("test"), "BigDecimal not supported");
|
|
||||||
assertThrowsUnsupportedAndExpectErrorMessage(() -> r.getBinaryStream("test"), "BinaryStream not supported");
|
assertThrowsUnsupportedAndExpectErrorMessage(() -> r.getBinaryStream("test"), "BinaryStream not supported");
|
||||||
assertThrowsUnsupportedAndExpectErrorMessage(() -> r.getBinaryStream(1), "BinaryStream not supported");
|
assertThrowsUnsupportedAndExpectErrorMessage(() -> r.getBinaryStream(1), "BinaryStream not supported");
|
||||||
assertThrowsUnsupportedAndExpectErrorMessage(() -> r.getBlob("test"), "Blob not supported");
|
assertThrowsUnsupportedAndExpectErrorMessage(() -> r.getBlob("test"), "Blob not supported");
|
||||||
|
@ -1543,7 +1720,24 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private <T extends Number> void doWithQuery(String query, List<T> testValues,
|
||||||
|
CheckedBiConsumer<ResultSet, List<T>, SQLException> biConsumer) throws SQLException {
|
||||||
|
doWithQuery(() -> esJdbc(timeZoneId), query, testValues, biConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends Number> void doWithQuery(CheckedSupplier<Connection, SQLException> con, String query, List<T> testValues,
|
||||||
|
CheckedBiConsumer<ResultSet, List<T>, SQLException> biConsumer)
|
||||||
|
throws SQLException {
|
||||||
|
try (Connection connection = con.get()) {
|
||||||
|
try (PreparedStatement statement = connection.prepareStatement(query)) {
|
||||||
|
try (ResultSet results = statement.executeQuery()) {
|
||||||
|
biConsumer.accept(results, testValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected static void createIndex(String index) throws Exception {
|
protected static void createIndex(String index) throws Exception {
|
||||||
Request request = new Request("PUT", "/" + index);
|
Request request = new Request("PUT", "/" + index);
|
||||||
XContentBuilder createIndex = JsonXContent.contentBuilder().startObject();
|
XContentBuilder createIndex = JsonXContent.contentBuilder().startObject();
|
||||||
|
@ -1636,111 +1830,53 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createTestDataForByteValueTests(byte random1, byte random2, byte random3) throws Exception {
|
/**
|
||||||
|
* Creates test data for type specific get* method. It returns a list with the randomly generated test values.
|
||||||
|
*/
|
||||||
|
private <T extends Number> List<T> createTestDataForNumericValueTests(Supplier<T> numberGenerator) throws Exception {
|
||||||
|
T random1 = numberGenerator.get();
|
||||||
|
T random2 = randomValueOtherThan(random1, numberGenerator);
|
||||||
|
T random3 = randomValueOtherThanMany(Arrays.asList(random1, random2)::contains, numberGenerator);
|
||||||
|
|
||||||
|
Class<? extends Number> clazz = random1.getClass();
|
||||||
|
String primitiveName = clazz.getSimpleName().toLowerCase(Locale.ROOT);
|
||||||
|
|
||||||
createIndex("test");
|
createIndex("test");
|
||||||
updateMapping("test", builder -> {
|
updateMapping("test", builder -> {
|
||||||
builder.startObject("test_byte").field("type", "byte").endObject();
|
builder.startObject("test_" + primitiveName).field("type", primitiveName).endObject();
|
||||||
builder.startObject("test_null_byte").field("type", "byte").endObject();
|
builder.startObject("test_null_" + primitiveName).field("type", primitiveName).endObject();
|
||||||
builder.startObject("test_keyword").field("type", "keyword").endObject();
|
builder.startObject("test_keyword").field("type", "keyword").endObject();
|
||||||
});
|
});
|
||||||
|
|
||||||
index("test", "1", builder -> {
|
index("test", "1", builder -> {
|
||||||
builder.field("test_byte", random1);
|
builder.field("test_" + primitiveName, random1);
|
||||||
builder.field("test_null_byte", (Byte) null);
|
builder.field("test_null_" + primitiveName, (Byte) null);
|
||||||
});
|
});
|
||||||
index("test", "2", builder -> {
|
index("test", "2", builder -> {
|
||||||
builder.field("test_byte", random2);
|
builder.field("test_" + primitiveName, random2);
|
||||||
builder.field("test_keyword", random3);
|
builder.field("test_keyword", random3);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return Arrays.asList(random1, random2, random3);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createTestDataForShortValueTests(short random1, short random2, short random3) throws Exception {
|
private void createTestDataForBooleanValueTests() throws Exception {
|
||||||
createIndex("test");
|
createIndex("test");
|
||||||
updateMapping("test", builder -> {
|
updateMapping("test", builder -> {
|
||||||
builder.startObject("test_short").field("type", "short").endObject();
|
builder.startObject("test_boolean").field("type", "boolean").endObject();
|
||||||
builder.startObject("test_null_short").field("type", "short").endObject();
|
builder.startObject("test_null_boolean").field("type", "boolean").endObject();
|
||||||
builder.startObject("test_keyword").field("type", "keyword").endObject();
|
builder.startObject("test_keyword").field("type", "keyword").endObject();
|
||||||
});
|
});
|
||||||
|
|
||||||
index("test", "1", builder -> {
|
index("test", "1", builder -> {
|
||||||
builder.field("test_short", random1);
|
builder.field("test_boolean", Boolean.TRUE);
|
||||||
builder.field("test_null_short", (Short) null);
|
builder.field("test_null_boolean", (Boolean) null);
|
||||||
|
builder.field("test_keyword", "1");
|
||||||
});
|
});
|
||||||
index("test", "2", builder -> {
|
index("test", "2", builder -> {
|
||||||
builder.field("test_short", random2);
|
builder.field("test_boolean", Boolean.FALSE);
|
||||||
builder.field("test_keyword", random3);
|
builder.field("test_null_boolean", (Boolean) null);
|
||||||
});
|
builder.field("test_keyword", "0");
|
||||||
}
|
|
||||||
|
|
||||||
private void createTestDataForIntegerValueTests(int random1, int random2, int random3) throws Exception {
|
|
||||||
createIndex("test");
|
|
||||||
updateMapping("test", builder -> {
|
|
||||||
builder.startObject("test_integer").field("type", "integer").endObject();
|
|
||||||
builder.startObject("test_null_integer").field("type", "integer").endObject();
|
|
||||||
builder.startObject("test_keyword").field("type", "keyword").endObject();
|
|
||||||
});
|
|
||||||
|
|
||||||
index("test", "1", builder -> {
|
|
||||||
builder.field("test_integer", random1);
|
|
||||||
builder.field("test_null_integer", (Integer) null);
|
|
||||||
});
|
|
||||||
index("test", "2", builder -> {
|
|
||||||
builder.field("test_integer", random2);
|
|
||||||
builder.field("test_keyword", random3);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createTestDataForLongValueTests(long random1, long random2, long random3) throws Exception {
|
|
||||||
createIndex("test");
|
|
||||||
updateMapping("test", builder -> {
|
|
||||||
builder.startObject("test_long").field("type", "long").endObject();
|
|
||||||
builder.startObject("test_null_long").field("type", "long").endObject();
|
|
||||||
builder.startObject("test_keyword").field("type", "keyword").endObject();
|
|
||||||
});
|
|
||||||
|
|
||||||
index("test", "1", builder -> {
|
|
||||||
builder.field("test_long", random1);
|
|
||||||
builder.field("test_null_long", (Long) null);
|
|
||||||
});
|
|
||||||
index("test", "2", builder -> {
|
|
||||||
builder.field("test_long", random2);
|
|
||||||
builder.field("test_keyword", random3);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createTestDataForDoubleValueTests(double random1, double random2, double random3) throws Exception {
|
|
||||||
createIndex("test");
|
|
||||||
updateMapping("test", builder -> {
|
|
||||||
builder.startObject("test_double").field("type", "double").endObject();
|
|
||||||
builder.startObject("test_null_double").field("type", "double").endObject();
|
|
||||||
builder.startObject("test_keyword").field("type", "keyword").endObject();
|
|
||||||
});
|
|
||||||
|
|
||||||
index("test", "1", builder -> {
|
|
||||||
builder.field("test_double", random1);
|
|
||||||
builder.field("test_null_double", (Double) null);
|
|
||||||
});
|
|
||||||
index("test", "2", builder -> {
|
|
||||||
builder.field("test_double", random2);
|
|
||||||
builder.field("test_keyword", random3);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createTestDataForFloatValueTests(float random1, float random2, float random3) throws Exception {
|
|
||||||
createIndex("test");
|
|
||||||
updateMapping("test", builder -> {
|
|
||||||
builder.startObject("test_float").field("type", "float").endObject();
|
|
||||||
builder.startObject("test_null_float").field("type", "float").endObject();
|
|
||||||
builder.startObject("test_keyword").field("type", "keyword").endObject();
|
|
||||||
});
|
|
||||||
|
|
||||||
index("test", "1", builder -> {
|
|
||||||
builder.field("test_float", random1);
|
|
||||||
builder.field("test_null_float", (Double) null);
|
|
||||||
});
|
|
||||||
index("test", "2", builder -> {
|
|
||||||
builder.field("test_float", random2);
|
|
||||||
builder.field("test_keyword", random3);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue