diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcPreparedStatement.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcPreparedStatement.java index 39d942362d7..08b766b0b69 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcPreparedStatement.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcPreparedStatement.java @@ -123,7 +123,13 @@ class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement { @Override 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 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 3f4d055aff0..6d9e33b91f2 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 @@ -5,6 +5,8 @@ */ package org.elasticsearch.xpack.sql.jdbc; +import org.elasticsearch.common.SuppressForbidden; + import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; @@ -300,7 +302,6 @@ class JdbcResultSet implements ResultSet, JdbcWrapper { } try { - return JdbcDateUtils.asDate(val.toString()); } catch (Exception e) { throw new SQLException( @@ -525,7 +526,11 @@ class JdbcResultSet implements ResultSet, JdbcWrapper { @Override @Deprecated 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 @@ -546,8 +551,9 @@ class JdbcResultSet implements ResultSet, JdbcWrapper { @Override @Deprecated + @SuppressForbidden(reason="implementing deprecated method") public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException { - throw new SQLFeatureNotSupportedException("BigDecimal not supported"); + return getBigDecimal(column(columnLabel), scale); } @Override @@ -594,12 +600,12 @@ class JdbcResultSet implements ResultSet, JdbcWrapper { @Override public BigDecimal getBigDecimal(int columnIndex) throws SQLException { - throw new SQLFeatureNotSupportedException("BigDecimal not supported"); + return convert(columnIndex, BigDecimal.class); } @Override public BigDecimal getBigDecimal(String columnLabel) throws SQLException { - throw new SQLFeatureNotSupportedException("BigDecimal not supported"); + return getBigDecimal(column(columnLabel)); } @Override diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/TypeConverter.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/TypeConverter.java index 888fb8632d2..a36ae1612f4 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/TypeConverter.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/TypeConverter.java @@ -10,6 +10,7 @@ import org.elasticsearch.geometry.utils.WellKnownText; import org.elasticsearch.xpack.sql.proto.StringUtils; import java.io.IOException; +import java.math.BigDecimal; import java.sql.Date; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; @@ -178,6 +179,9 @@ final class TypeConverter { if (type == byte[].class) { return (T) asByteArray(val, columnType, typeString); } + if (type == BigDecimal.class) { + return (T) asBigDecimal(val, columnType, typeString); + } // // JDK 8 types // @@ -537,6 +541,36 @@ final class TypeConverter { 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 { throw new SQLFeatureNotSupportedException(); } diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/PreparedStatementTestCase.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/PreparedStatementTestCase.java index 34d32db16f4..161f4a201fc 100644 --- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/PreparedStatementTestCase.java +++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/PreparedStatementTestCase.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.qa.jdbc; import org.elasticsearch.common.collect.Tuple; +import java.math.BigDecimal; import java.sql.Connection; import java.sql.JDBCType; import java.sql.ParameterMetaData; @@ -16,16 +17,12 @@ import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.SQLSyntaxErrorException; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.startsWith; public class PreparedStatementTestCase extends JdbcIntegrationTestCase { public void testSupportedTypes() throws Exception { - index("library", builder -> { - builder.field("name", "Don Quixote"); - builder.field("page_count", 1072); - }); - String stringVal = randomAlphaOfLength(randomIntBetween(0, 1000)); int intVal = randomInt(); long longVal = randomLong(); @@ -34,10 +31,11 @@ public class PreparedStatementTestCase extends JdbcIntegrationTestCase { boolean booleanVal = randomBoolean(); byte byteVal = randomByte(); short shortVal = randomShort(); + BigDecimal bigDecimalVal = BigDecimal.valueOf(randomDouble()); try (Connection connection = esJdbc()) { try (PreparedStatement statement = connection.prepareStatement( - "SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, name FROM library WHERE page_count=?")) { + "SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ?")) { statement.setString(1, stringVal); statement.setInt(2, intVal); statement.setLong(3, longVal); @@ -47,7 +45,7 @@ public class PreparedStatementTestCase extends JdbcIntegrationTestCase { statement.setBoolean(7, booleanVal); statement.setByte(8, byteVal); statement.setShort(9, shortVal); - statement.setInt(10, 1072); + statement.setBigDecimal(10, bigDecimalVal); try (ResultSet results = statement.executeQuery()) { ResultSetMetaData resultSetMetaData = results.getMetaData(); @@ -67,13 +65,23 @@ public class PreparedStatementTestCase extends JdbcIntegrationTestCase { assertEquals(booleanVal, results.getBoolean(7)); assertEquals(byteVal, results.getByte(8)); assertEquals(shortVal, results.getShort(9)); - assertEquals("Don Quixote", results.getString(10)); + assertEquals(bigDecimalVal, results.getBigDecimal(10)); 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 { index("library", builder -> { builder.field("name", "Don Quixote"); 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 e59316ed66f..546d243bc47 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 @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.sql.qa.jdbc; import org.elasticsearch.client.Request; +import org.elasticsearch.common.CheckedBiConsumer; import org.elasticsearch.common.CheckedBiFunction; import org.elasticsearch.common.CheckedConsumer; import org.elasticsearch.common.CheckedFunction; @@ -14,12 +15,14 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.json.JsonXContent; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.sql.jdbc.EsType; import org.junit.Before; import java.io.IOException; import java.io.InputStream; import java.io.Reader; +import java.math.BigDecimal; import java.sql.Blob; import java.sql.Clob; import java.sql.Connection; @@ -41,6 +44,7 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; @@ -153,12 +157,11 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase { // Byte values testing public void testGettingValidByteWithoutCasting() throws Exception { - byte random1 = randomByte(); - byte random2 = randomValueOtherThan(random1, () -> randomByte()); - byte random3 = randomValueOtherThanMany(Arrays.asList(random1, random2)::contains, () -> randomByte()); - - createTestDataForByteValueTests(random1, random2, random3); - + List byteTestValues = createTestDataForNumericValueTests(ESTestCase::randomByte); + byte random1 = byteTestValues.get(0); + byte random2 = byteTestValues.get(1); + byte random3 = byteTestValues.get(2); + doWithQuery("SELECT test_byte, test_null_byte, test_keyword FROM test", (results) -> { ResultSetMetaData resultSetMetaData = results.getMetaData(); @@ -282,12 +285,11 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase { // Short values testing public void testGettingValidShortWithoutCasting() throws Exception { - short random1 = randomShort(); - short random2 = randomValueOtherThan(random1, () -> randomShort()); - short random3 = randomValueOtherThanMany(Arrays.asList(random1, random2)::contains, () -> randomShort()); - - createTestDataForShortValueTests(random1, random2, random3); - + List shortTestValues = createTestDataForNumericValueTests(ESTestCase::randomShort); + short random1 = shortTestValues.get(0); + short random2 = shortTestValues.get(1); + short random3 = shortTestValues.get(2); + doWithQuery("SELECT test_short, test_null_short, test_keyword FROM test", (results) -> { ResultSetMetaData resultSetMetaData = results.getMetaData(); @@ -405,12 +407,11 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase { // Integer values testing public void testGettingValidIntegerWithoutCasting() throws Exception { - int random1 = randomInt(); - int random2 = randomValueOtherThan(random1, () -> randomInt()); - int random3 = randomValueOtherThanMany(Arrays.asList(random1, random2)::contains, () -> randomInt()); - - createTestDataForIntegerValueTests(random1, random2, random3); - + List integerTestValues = createTestDataForNumericValueTests(ESTestCase::randomInt); + int random1 = integerTestValues.get(0); + int random2 = integerTestValues.get(1); + int random3 = integerTestValues.get(2); + doWithQuery("SELECT test_integer,test_null_integer,test_keyword FROM test", (results) -> { ResultSetMetaData resultSetMetaData = results.getMetaData(); @@ -520,12 +521,11 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase { // Long values testing public void testGettingValidLongWithoutCasting() throws Exception { - long random1 = randomLong(); - long random2 = randomValueOtherThan(random1, () -> randomLong()); - long random3 = randomValueOtherThanMany(Arrays.asList(random1, random2)::contains, () -> randomLong()); - - createTestDataForLongValueTests(random1, random2, random3); - + List longTestValues = createTestDataForNumericValueTests(ESTestCase::randomLong); + long random1 = longTestValues.get(0); + long random2 = longTestValues.get(1); + long random3 = longTestValues.get(2); + doWithQuery("SELECT test_long, test_null_long, test_keyword FROM test", (results) -> { ResultSetMetaData resultSetMetaData = results.getMetaData(); @@ -622,12 +622,11 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase { // Double values testing public void testGettingValidDoubleWithoutCasting() throws Exception { - double random1 = randomDouble(); - double random2 = randomValueOtherThan(random1, () -> randomDouble()); - double random3 = randomValueOtherThanMany(Arrays.asList(random1, random2)::contains, () -> randomDouble()); - - createTestDataForDoubleValueTests(random1, random2, random3); - + List doubleTestValues = createTestDataForNumericValueTests(ESTestCase::randomDouble); + double random1 = doubleTestValues.get(0); + double random2 = doubleTestValues.get(1); + double random3 = doubleTestValues.get(2); + doWithQuery("SELECT test_double, test_null_double, test_keyword FROM test", (results) -> { ResultSetMetaData resultSetMetaData = results.getMetaData(); @@ -711,12 +710,11 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase { // Float values testing public void testGettingValidFloatWithoutCasting() throws Exception { - float random1 = randomFloat(); - float random2 = randomValueOtherThan(random1, () -> randomFloat()); - float random3 = randomValueOtherThanMany(Arrays.asList(random1, random2)::contains, () -> randomFloat()); - - createTestDataForFloatValueTests(random1, random2, random3); - + List floatTestValues = createTestDataForNumericValueTests(ESTestCase::randomFloat); + float random1 = floatTestValues.get(0); + float random2 = floatTestValues.get(1); + float random3 = floatTestValues.get(2); + doWithQuery("SELECT test_float, test_null_float, test_keyword FROM test", (results) -> { ResultSetMetaData resultSetMetaData = results.getMetaData(); @@ -791,7 +789,188 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase { sqle.getMessage()); }); } - + + // + // BigDecimal fetching testing + // + static final Map, Integer> JAVA_TO_SQL_NUMERIC_TYPES_MAP = new HashMap, 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 void validateBigDecimalWithoutCasting(ResultSet results, List testValues) + throws SQLException { + + ResultSetMetaData resultSetMetaData = results.getMetaData(); + + Class 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 byteTestValues = createTestDataForNumericValueTests(ESTestCase::randomByte); + doWithQuery("SELECT test_byte, test_null_byte, test_keyword FROM test", byteTestValues, + ResultSetTestCase::validateBigDecimalWithoutCasting); + } + public void testGettingValidBigDecimalFromShortWithoutCasting() throws Exception { + List shortTestValues = createTestDataForNumericValueTests(ESTestCase::randomShort); + doWithQuery("SELECT test_short, test_null_short, test_keyword FROM test", shortTestValues, + ResultSetTestCase::validateBigDecimalWithoutCasting); + } + public void testGettingValidBigDecimalFromIntegerWithoutCasting() throws Exception { + List integerTestValues = createTestDataForNumericValueTests(ESTestCase::randomInt); + doWithQuery("SELECT test_integer, test_null_integer, test_keyword FROM test", integerTestValues, + ResultSetTestCase::validateBigDecimalWithoutCasting); + } + public void testGettingValidBigDecimalFromLongWithoutCasting() throws Exception { + List longTestValues = createTestDataForNumericValueTests(ESTestCase::randomLong); + doWithQuery("SELECT test_long, test_null_long, test_keyword FROM test", longTestValues, + ResultSetTestCase::validateBigDecimalWithoutCasting); + } + public void testGettingValidBigDecimalFromFloatWithoutCasting() throws Exception { + List floatTestValues = createTestDataForNumericValueTests(ESTestCase::randomFloat); + doWithQuery("SELECT test_float, test_null_float, test_keyword FROM test", floatTestValues, + ResultSetTestCase::validateBigDecimalWithoutCasting); + } + public void testGettingValidBigDecimalFromDoubleWithoutCasting() throws Exception { + List doubleTestValues = createTestDataForNumericValueTests(ESTestCase::randomDouble); + doWithQuery("SELECT test_double, test_null_double, test_keyword FROM test", doubleTestValues, + ResultSetTestCase::validateBigDecimalWithoutCasting); + } + + public void testGettingValidBigDecimalWithCasting() throws Exception { + Map map = createTestDataForNumericValueTypes(() -> randomDouble()); + + doWithQuery(SELECT_WILDCARD, (results) -> { + results.next(); + for (Entry 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 { createIndex("test"); updateMappingForNumericValuesTests("test"); @@ -1361,8 +1540,6 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase { assertThrowsUnsupportedAndExpectErrorMessage(() -> r.getAsciiStream(1), "AsciiStream not supported"); assertThrowsUnsupportedAndExpectErrorMessage(() -> r.getArray("test"), "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(1), "BinaryStream not supported"); assertThrowsUnsupportedAndExpectErrorMessage(() -> r.getBlob("test"), "Blob not supported"); @@ -1543,7 +1720,24 @@ public class ResultSetTestCase extends JdbcIntegrationTestCase { } } } - + + private void doWithQuery(String query, List testValues, + CheckedBiConsumer, SQLException> biConsumer) throws SQLException { + doWithQuery(() -> esJdbc(timeZoneId), query, testValues, biConsumer); + } + + private void doWithQuery(CheckedSupplier con, String query, List testValues, + CheckedBiConsumer, 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 { Request request = new Request("PUT", "/" + index); 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 List createTestDataForNumericValueTests(Supplier numberGenerator) throws Exception { + T random1 = numberGenerator.get(); + T random2 = randomValueOtherThan(random1, numberGenerator); + T random3 = randomValueOtherThanMany(Arrays.asList(random1, random2)::contains, numberGenerator); + + Class clazz = random1.getClass(); + String primitiveName = clazz.getSimpleName().toLowerCase(Locale.ROOT); + createIndex("test"); updateMapping("test", builder -> { - builder.startObject("test_byte").field("type", "byte").endObject(); - builder.startObject("test_null_byte").field("type", "byte").endObject(); + builder.startObject("test_" + primitiveName).field("type", primitiveName).endObject(); + builder.startObject("test_null_" + primitiveName).field("type", primitiveName).endObject(); builder.startObject("test_keyword").field("type", "keyword").endObject(); }); - + index("test", "1", builder -> { - builder.field("test_byte", random1); - builder.field("test_null_byte", (Byte) null); + builder.field("test_" + primitiveName, random1); + builder.field("test_null_" + primitiveName, (Byte) null); }); index("test", "2", builder -> { - builder.field("test_byte", random2); + builder.field("test_" + primitiveName, random2); 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"); updateMapping("test", builder -> { - builder.startObject("test_short").field("type", "short").endObject(); - builder.startObject("test_null_short").field("type", "short").endObject(); + builder.startObject("test_boolean").field("type", "boolean").endObject(); + builder.startObject("test_null_boolean").field("type", "boolean").endObject(); builder.startObject("test_keyword").field("type", "keyword").endObject(); }); index("test", "1", builder -> { - builder.field("test_short", random1); - builder.field("test_null_short", (Short) null); + builder.field("test_boolean", Boolean.TRUE); + builder.field("test_null_boolean", (Boolean) null); + builder.field("test_keyword", "1"); }); index("test", "2", builder -> { - builder.field("test_short", random2); - builder.field("test_keyword", random3); - }); - } - - 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); + builder.field("test_boolean", Boolean.FALSE); + builder.field("test_null_boolean", (Boolean) null); + builder.field("test_keyword", "0"); }); }