From 400db4f37df1b3806b7fa8295ddd18c67eb7f27d Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Wed, 27 Jun 2018 10:05:39 +0300 Subject: [PATCH] JDBC driver prepared statement set* methods (#31494) Added setObject functionality and tests for it --- .../sql/jdbc/jdbc/JdbcPreparedStatement.java | 202 +++++- .../xpack/sql/jdbc/jdbc/JdbcResultSet.java | 8 - .../xpack/sql/jdbc/jdbc/TypeConverter.java | 74 ++- .../jdbc/jdbc/JdbcPreparedStatementTests.java | 582 ++++++++++++++++++ .../xpack/sql/type/DataType.java | 20 +- 5 files changed, 837 insertions(+), 49 deletions(-) create mode 100644 x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatementTests.java diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatement.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatement.java index 5ace03ff8a3..bae4260ac2b 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatement.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatement.java @@ -5,6 +5,8 @@ */ package org.elasticsearch.xpack.sql.jdbc.jdbc; +import org.elasticsearch.xpack.sql.type.DataType; + import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; @@ -21,13 +23,24 @@ import java.sql.Ref; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.RowId; +import java.sql.SQLDataException; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.sql.SQLXML; +import java.sql.Struct; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; +import java.util.List; +import java.util.Locale; class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement { final PreparedQuery query; @@ -74,67 +87,67 @@ class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement { @Override public void setBoolean(int parameterIndex, boolean x) throws SQLException { - setParam(parameterIndex, x, Types.BOOLEAN); + setObject(parameterIndex, x, Types.BOOLEAN); } @Override public void setByte(int parameterIndex, byte x) throws SQLException { - setParam(parameterIndex, x, Types.TINYINT); + setObject(parameterIndex, x, Types.TINYINT); } @Override public void setShort(int parameterIndex, short x) throws SQLException { - setParam(parameterIndex, x, Types.SMALLINT); + setObject(parameterIndex, x, Types.SMALLINT); } @Override public void setInt(int parameterIndex, int x) throws SQLException { - setParam(parameterIndex, x, Types.INTEGER); + setObject(parameterIndex, x, Types.INTEGER); } @Override public void setLong(int parameterIndex, long x) throws SQLException { - setParam(parameterIndex, x, Types.BIGINT); + setObject(parameterIndex, x, Types.BIGINT); } @Override public void setFloat(int parameterIndex, float x) throws SQLException { - setParam(parameterIndex, x, Types.REAL); + setObject(parameterIndex, x, Types.REAL); } @Override public void setDouble(int parameterIndex, double x) throws SQLException { - setParam(parameterIndex, x, Types.DOUBLE); + setObject(parameterIndex, x, Types.DOUBLE); } @Override public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { - throw new SQLFeatureNotSupportedException("BigDecimal not supported"); + setObject(parameterIndex, x, Types.BIGINT); } @Override public void setString(int parameterIndex, String x) throws SQLException { - setParam(parameterIndex, x, Types.VARCHAR); + setObject(parameterIndex, x, Types.VARCHAR); } @Override public void setBytes(int parameterIndex, byte[] x) throws SQLException { - throw new UnsupportedOperationException("Bytes not implemented yet"); + setObject(parameterIndex, x, Types.VARBINARY); } @Override public void setDate(int parameterIndex, Date x) throws SQLException { - throw new UnsupportedOperationException("Date/Time not implemented yet"); + setObject(parameterIndex, x, Types.TIMESTAMP); } @Override public void setTime(int parameterIndex, Time x) throws SQLException { - throw new UnsupportedOperationException("Date/Time not implemented yet"); + setObject(parameterIndex, x, Types.TIMESTAMP); } @Override public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { - throw new UnsupportedOperationException("Date/Time not implemented yet"); + setObject(parameterIndex, x, Types.TIMESTAMP); } @Override @@ -161,12 +174,22 @@ class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement { @Override public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { - throw new UnsupportedOperationException("Object not implemented yet"); + // the value of scaleOrLength parameter doesn't matter, as it's not used in the called method below + setObject(parameterIndex, x, targetSqlType, 0); } @Override public void setObject(int parameterIndex, Object x) throws SQLException { - throw new SQLFeatureNotSupportedException("CharacterStream not supported"); + if (x == null) { + setParam(parameterIndex, null, Types.NULL); + return; + } + + // check also here the unsupported types so that any unsupported interfaces ({@code java.sql.Struct}, + // {@code java.sql.Array} etc) will generate the correct exception message. Otherwise, the method call + // {@code TypeConverter.fromJavaToJDBC(x.getClass())} will report the implementing class as not being supported. + checkKnownUnsupportedTypes(x); + setObject(parameterIndex, x, TypeConverter.fromJavaToJDBC(x.getClass()).getVendorTypeNumber(), 0); } @Override @@ -181,22 +204,22 @@ class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement { @Override public void setRef(int parameterIndex, Ref x) throws SQLException { - throw new SQLFeatureNotSupportedException("Ref not supported"); + setObject(parameterIndex, x); } @Override public void setBlob(int parameterIndex, Blob x) throws SQLException { - throw new SQLFeatureNotSupportedException("Blob not supported"); + setObject(parameterIndex, x); } @Override public void setClob(int parameterIndex, Clob x) throws SQLException { - throw new SQLFeatureNotSupportedException("Clob not supported"); + setObject(parameterIndex, x); } @Override public void setArray(int parameterIndex, Array x) throws SQLException { - throw new SQLFeatureNotSupportedException("Array not supported"); + setObject(parameterIndex, x); } @Override @@ -206,17 +229,44 @@ class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement { @Override public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { - throw new UnsupportedOperationException("Dates not implemented yet"); + if (cal == null) { + setObject(parameterIndex, x, Types.TIMESTAMP); + return; + } + if (x == null) { + setNull(parameterIndex, Types.TIMESTAMP); + return; + } + // converting to UTC since this is what ES is storing internally + setObject(parameterIndex, new Date(TypeConverter.convertFromCalendarToUTC(x.getTime(), cal)), Types.TIMESTAMP); } @Override public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { - throw new UnsupportedOperationException("Dates not implemented yet"); + if (cal == null) { + setObject(parameterIndex, x, Types.TIMESTAMP); + return; + } + if (x == null) { + setNull(parameterIndex, Types.TIMESTAMP); + return; + } + // converting to UTC since this is what ES is storing internally + setObject(parameterIndex, new Time(TypeConverter.convertFromCalendarToUTC(x.getTime(), cal)), Types.TIMESTAMP); } @Override public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { - throw new UnsupportedOperationException("Dates not implemented yet"); + if (cal == null) { + setObject(parameterIndex, x, Types.TIMESTAMP); + return; + } + if (x == null) { + setNull(parameterIndex, Types.TIMESTAMP); + return; + } + // converting to UTC since this is what ES is storing internally + setObject(parameterIndex, new Timestamp(TypeConverter.convertFromCalendarToUTC(x.getTime(), cal)), Types.TIMESTAMP); } @Override @@ -226,7 +276,7 @@ class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement { @Override public void setURL(int parameterIndex, URL x) throws SQLException { - throw new SQLFeatureNotSupportedException("Datalink not supported"); + setObject(parameterIndex, x); } @Override @@ -236,7 +286,7 @@ class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement { @Override public void setRowId(int parameterIndex, RowId x) throws SQLException { - throw new SQLFeatureNotSupportedException("RowId not supported"); + setObject(parameterIndex, x); } @Override @@ -251,7 +301,7 @@ class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement { @Override public void setNClob(int parameterIndex, NClob value) throws SQLException { - throw new SQLFeatureNotSupportedException("NClob not supported"); + setObject(parameterIndex, value); } @Override @@ -271,12 +321,108 @@ class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement { @Override public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { - throw new SQLFeatureNotSupportedException("SQLXML not supported"); + setObject(parameterIndex, xmlObject); } - + @Override public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException { - throw new UnsupportedOperationException("Object not implemented yet"); + checkOpen(); + + JDBCType targetJDBCType; + try { + // this is also a way to check early for the validity of the desired sql type + targetJDBCType = JDBCType.valueOf(targetSqlType); + } catch (IllegalArgumentException e) { + throw new SQLDataException(e.getMessage()); + } + + // set the null value on the type and exit + if (x == null) { + setParam(parameterIndex, null, targetSqlType); + return; + } + + checkKnownUnsupportedTypes(x); + if (x instanceof byte[]) { + if (targetJDBCType != JDBCType.VARBINARY) { + throw new SQLFeatureNotSupportedException( + "Conversion from type byte[] to " + targetJDBCType + " not supported"); + } + setParam(parameterIndex, x, Types.VARBINARY); + return; + } + + if (x instanceof Timestamp + || x instanceof Calendar + || x instanceof Date + || x instanceof LocalDateTime + || x instanceof Time + || x instanceof java.util.Date) + { + if (targetJDBCType == JDBCType.TIMESTAMP) { + // converting to {@code java.util.Date} because this is the type supported by {@code XContentBuilder} for serialization + java.util.Date dateToSet; + if (x instanceof Timestamp) { + dateToSet = new java.util.Date(((Timestamp) x).getTime()); + } else if (x instanceof Calendar) { + dateToSet = ((Calendar) x).getTime(); + } else if (x instanceof Date) { + dateToSet = new java.util.Date(((Date) x).getTime()); + } else if (x instanceof LocalDateTime){ + LocalDateTime ldt = (LocalDateTime) x; + Calendar cal = getDefaultCalendar(); + cal.set(ldt.getYear(), ldt.getMonthValue() - 1, ldt.getDayOfMonth(), ldt.getHour(), ldt.getMinute(), ldt.getSecond()); + + dateToSet = cal.getTime(); + } else if (x instanceof Time) { + dateToSet = new java.util.Date(((Time) x).getTime()); + } else { + dateToSet = (java.util.Date) x; + } + + setParam(parameterIndex, dateToSet, Types.TIMESTAMP); + return; + } else if (targetJDBCType == JDBCType.VARCHAR) { + setParam(parameterIndex, String.valueOf(x), Types.VARCHAR); + return; + } + // anything else other than VARCHAR and TIMESTAMP is not supported in this JDBC driver + throw new SQLFeatureNotSupportedException( + "Conversion from type " + x.getClass().getName() + " to " + targetJDBCType + " not supported"); + } + + if (x instanceof Boolean + || x instanceof Byte + || x instanceof Short + || x instanceof Integer + || x instanceof Long + || x instanceof Float + || x instanceof Double + || x instanceof String) { + setParam(parameterIndex, + TypeConverter.convert(x, TypeConverter.fromJavaToJDBC(x.getClass()), DataType.fromJdbcTypeToJava(targetJDBCType)), + targetSqlType); + return; + } + + throw new SQLFeatureNotSupportedException( + "Conversion from type " + x.getClass().getName() + " to " + targetJDBCType + " not supported"); + } + + private void checkKnownUnsupportedTypes(Object x) throws SQLFeatureNotSupportedException { + List> unsupportedTypes = new ArrayList>(Arrays.asList(Struct.class, Array.class, SQLXML.class, + RowId.class, Ref.class, Blob.class, NClob.class, Clob.class, LocalDate.class, LocalTime.class, + OffsetTime.class, OffsetDateTime.class, URL.class, BigDecimal.class)); + + for (Class clazz:unsupportedTypes) { + if (clazz.isAssignableFrom(x.getClass())) { + throw new SQLFeatureNotSupportedException("Objects of type " + clazz.getName() + " are not supported"); + } + } + } + + private Calendar getDefaultCalendar() { + return Calendar.getInstance(cfg.timeZone(), Locale.ROOT); } @Override diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSet.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSet.java index c92ac9c5ac9..351ac73a88f 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSet.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcResultSet.java @@ -359,14 +359,6 @@ class JdbcResultSet implements ResultSet, JdbcWrapper { return null; } - if (type != null && type.isInstance(val)) { - try { - return type.cast(val); - } catch (ClassCastException cce) { - throw new SQLException("unable to convert column " + columnIndex + " to " + type, cce); - } - } - JDBCType columnType = cursor.columns().get(columnIndex - 1).type; return TypeConverter.convert(val, columnType, type); diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeConverter.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeConverter.java index a1fa04ef1af..1e24a03c8b3 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeConverter.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/TypeConverter.java @@ -10,7 +10,9 @@ import org.elasticsearch.xpack.sql.type.DataType; import java.sql.Date; import java.sql.JDBCType; +import java.sql.SQLDataException; import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; import java.sql.Time; import java.sql.Timestamp; import java.time.LocalDate; @@ -18,10 +20,17 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.OffsetTime; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.Arrays; import java.util.Calendar; +import java.util.Collections; import java.util.GregorianCalendar; import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; import java.util.function.Function; +import java.util.stream.Collectors; import static java.lang.String.format; import static java.util.Calendar.DAY_OF_MONTH; @@ -48,6 +57,22 @@ final class TypeConverter { } private static final long DAY_IN_MILLIS = 60 * 60 * 24; + private static final Map, JDBCType> javaToJDBC; + + static { + Map, JDBCType> aMap = Arrays.stream(DataType.values()) + .filter(dataType -> dataType.javaClass() != null + && dataType != DataType.HALF_FLOAT + && dataType != DataType.SCALED_FLOAT + && dataType != DataType.TEXT) + .collect(Collectors.toMap(dataType -> dataType.javaClass(), dataType -> dataType.jdbcType)); + // apart from the mappings in {@code DataType} three more Java classes can be mapped to a {@code JDBCType.TIMESTAMP} + // according to B-4 table from the jdbc4.2 spec + aMap.put(Calendar.class, JDBCType.TIMESTAMP); + aMap.put(java.util.Date.class, JDBCType.TIMESTAMP); + aMap.put(LocalDateTime.class, JDBCType.TIMESTAMP); + javaToJDBC = Collections.unmodifiableMap(aMap); + } /** * Converts millisecond after epoc to date @@ -94,6 +119,20 @@ final class TypeConverter { c.setTimeInMillis(initial); } } + + static long convertFromCalendarToUTC(long value, Calendar cal) { + if (cal == null) { + return value; + } + Calendar c = (Calendar) cal.clone(); + c.setTimeInMillis(value); + + ZonedDateTime convertedDateTime = ZonedDateTime + .ofInstant(c.toInstant(), c.getTimeZone().toZoneId()) + .withZoneSameLocal(ZoneOffset.UTC); + + return convertedDateTime.toInstant().toEpochMilli(); + } /** * Converts object val from columnType to type @@ -103,6 +142,15 @@ final class TypeConverter { if (type == null) { return (T) convert(val, columnType); } + + if (type.isInstance(val)) { + try { + return type.cast(val); + } catch (ClassCastException cce) { + throw new SQLDataException("Unable to convert " + val.getClass().getName() + " to " + columnType, cce); + } + } + if (type == String.class) { return (T) asString(convert(val, columnType)); } @@ -174,10 +222,10 @@ final class TypeConverter { // Convert unsupported exception to JdbcSQLException throw new JdbcSQLException(ex, ex.getMessage()); } - if (dataType.javaName == null) { + if (dataType.javaClass() == null) { throw new JdbcSQLException("Unsupported JDBC type [" + jdbcType + "]"); } - return dataType.javaName; + return dataType.javaClass().getName(); } /** @@ -228,6 +276,18 @@ final class TypeConverter { } return dataType.isSigned(); } + + + static JDBCType fromJavaToJDBC(Class clazz) throws SQLException { + for (Entry, JDBCType> e : javaToJDBC.entrySet()) { + // java.util.Calendar from {@code javaToJDBC} is an abstract class and this method can be used with concrete classes as well + if (e.getKey().isAssignableFrom(clazz)) { + return e.getValue(); + } + } + + throw new SQLFeatureNotSupportedException("Objects of type " + clazz.getName() + " are not supported"); + } private static Double doubleValue(Object v) { if (v instanceof String) { @@ -275,7 +335,7 @@ final class TypeConverter { case REAL: case FLOAT: case DOUBLE: - return Boolean.valueOf(Integer.signum(((Number) val).intValue()) == 0); + return Boolean.valueOf(Integer.signum(((Number) val).intValue()) != 0); default: throw new SQLException("Conversion from type [" + columnType + "] to [Boolean] not supported"); @@ -454,28 +514,28 @@ final class TypeConverter { private static byte safeToByte(long x) throws SQLException { if (x > Byte.MAX_VALUE || x < Byte.MIN_VALUE) { - throw new SQLException(format(Locale.ROOT, "Numeric %d out of range", Long.toString(x))); + throw new SQLException(format(Locale.ROOT, "Numeric %s out of range", Long.toString(x))); } return (byte) x; } private static short safeToShort(long x) throws SQLException { if (x > Short.MAX_VALUE || x < Short.MIN_VALUE) { - throw new SQLException(format(Locale.ROOT, "Numeric %d out of range", Long.toString(x))); + throw new SQLException(format(Locale.ROOT, "Numeric %s out of range", Long.toString(x))); } return (short) x; } private static int safeToInt(long x) throws SQLException { if (x > Integer.MAX_VALUE || x < Integer.MIN_VALUE) { - throw new SQLException(format(Locale.ROOT, "Numeric %d out of range", Long.toString(x))); + throw new SQLException(format(Locale.ROOT, "Numeric %s out of range", Long.toString(x))); } return (int) x; } private static long safeToLong(double x) throws SQLException { if (x > Long.MAX_VALUE || x < Long.MIN_VALUE) { - throw new SQLException(format(Locale.ROOT, "Numeric %d out of range", Double.toString(x))); + throw new SQLException(format(Locale.ROOT, "Numeric %s out of range", Double.toString(x))); } return Math.round(x); } diff --git a/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatementTests.java b/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatementTests.java new file mode 100644 index 00000000000..ad96825896e --- /dev/null +++ b/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatementTests.java @@ -0,0 +1,582 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.sql.jdbc.jdbc; + +import org.elasticsearch.test.ESTestCase; + +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.sql.JDBCType; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.sql.Struct; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.time.Clock; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.Map; + +import static java.sql.JDBCType.BIGINT; +import static java.sql.JDBCType.BOOLEAN; +import static java.sql.JDBCType.DOUBLE; +import static java.sql.JDBCType.FLOAT; +import static java.sql.JDBCType.INTEGER; +import static java.sql.JDBCType.REAL; +import static java.sql.JDBCType.SMALLINT; +import static java.sql.JDBCType.TIMESTAMP; +import static java.sql.JDBCType.TINYINT; +import static java.sql.JDBCType.VARBINARY; +import static java.sql.JDBCType.VARCHAR; + +public class JdbcPreparedStatementTests extends ESTestCase { + + public void testSettingBooleanValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + + jps.setBoolean(1, true); + assertEquals(true, value(jps)); + assertEquals(BOOLEAN, jdbcType(jps)); + + jps.setObject(1, false); + assertEquals(false, value(jps)); + assertEquals(BOOLEAN, jdbcType(jps)); + + jps.setObject(1, true, Types.BOOLEAN); + assertEquals(true, value(jps)); + assertEquals(BOOLEAN, jdbcType(jps)); + assertTrue(value(jps) instanceof Boolean); + + jps.setObject(1, true, Types.INTEGER); + assertEquals(1, value(jps)); + assertEquals(INTEGER, jdbcType(jps)); + + jps.setObject(1, true, Types.VARCHAR); + assertEquals("true", value(jps)); + assertEquals(VARCHAR, jdbcType(jps)); + } + + public void testThrownExceptionsWhenSettingBooleanValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + + SQLException sqle = expectThrows(SQLException.class, () -> jps.setObject(1, true, Types.TIMESTAMP)); + assertEquals("Conversion from type [BOOLEAN] to [Timestamp] not supported", sqle.getMessage()); + } + + public void testSettingStringValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + + jps.setString(1, "foo bar"); + assertEquals("foo bar", value(jps)); + assertEquals(VARCHAR, jdbcType(jps)); + + jps.setObject(1, "foo bar"); + assertEquals("foo bar", value(jps)); + assertEquals(VARCHAR, jdbcType(jps)); + + jps.setObject(1, "foo bar", Types.VARCHAR); + assertEquals("foo bar", value(jps)); + assertEquals(VARCHAR, jdbcType(jps)); + assertTrue(value(jps) instanceof String); + } + + public void testThrownExceptionsWhenSettingStringValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + + SQLException sqle = expectThrows(SQLException.class, () -> jps.setObject(1, "foo bar", Types.INTEGER)); + assertEquals("Conversion from type [VARCHAR] to [Integer] not supported", sqle.getMessage()); + } + + public void testSettingByteTypeValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + + jps.setByte(1, (byte) 6); + assertEquals((byte) 6, value(jps)); + assertEquals(TINYINT, jdbcType(jps)); + + jps.setObject(1, (byte) 6); + assertEquals((byte) 6, value(jps)); + assertEquals(TINYINT, jdbcType(jps)); + assertTrue(value(jps) instanceof Byte); + + jps.setObject(1, (byte) 0, Types.BOOLEAN); + assertEquals(false, value(jps)); + assertEquals(BOOLEAN, jdbcType(jps)); + + jps.setObject(1, (byte) 123, Types.BOOLEAN); + assertEquals(true, value(jps)); + assertEquals(BOOLEAN, jdbcType(jps)); + + jps.setObject(1, (byte) 123, Types.INTEGER); + assertEquals(123, value(jps)); + assertEquals(INTEGER, jdbcType(jps)); + + jps.setObject(1, (byte) -128, Types.DOUBLE); + assertEquals(-128.0, value(jps)); + assertEquals(DOUBLE, jdbcType(jps)); + } + + public void testThrownExceptionsWhenSettingByteTypeValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + + SQLException sqle = expectThrows(SQLException.class, () -> jps.setObject(1, (byte) 6, Types.TIMESTAMP)); + assertEquals("Conversion from type [TINYINT] to [Timestamp] not supported", sqle.getMessage()); + } + + public void testSettingShortTypeValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + + short someShort = randomShort(); + jps.setShort(1, someShort); + assertEquals(someShort, value(jps)); + assertEquals(SMALLINT, jdbcType(jps)); + + jps.setObject(1, someShort); + assertEquals(someShort, value(jps)); + assertEquals(SMALLINT, jdbcType(jps)); + assertTrue(value(jps) instanceof Short); + + jps.setObject(1, (short) 1, Types.BOOLEAN); + assertEquals(true, value(jps)); + assertEquals(BOOLEAN, jdbcType(jps)); + + jps.setObject(1, (short) -32700, Types.DOUBLE); + assertEquals(-32700.0, value(jps)); + assertEquals(DOUBLE, jdbcType(jps)); + + jps.setObject(1, someShort, Types.INTEGER); + assertEquals((int) someShort, value(jps)); + assertEquals(INTEGER, jdbcType(jps)); + } + + public void testThrownExceptionsWhenSettingShortTypeValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + + SQLException sqle = expectThrows(SQLException.class, () -> jps.setObject(1, (short) 6, Types.TIMESTAMP)); + assertEquals("Conversion from type [SMALLINT] to [Timestamp] not supported", sqle.getMessage()); + + sqle = expectThrows(SQLException.class, () -> jps.setObject(1, 256, Types.TINYINT)); + assertEquals("Numeric " + 256 + " out of range", sqle.getMessage()); + } + + public void testSettingIntegerValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + + int someInt = randomInt(); + jps.setInt(1, someInt); + assertEquals(someInt, value(jps)); + assertEquals(INTEGER, jdbcType(jps)); + + jps.setObject(1, someInt); + assertEquals(someInt, value(jps)); + assertEquals(INTEGER, jdbcType(jps)); + assertTrue(value(jps) instanceof Integer); + + jps.setObject(1, someInt, Types.VARCHAR); + assertEquals(String.valueOf(someInt), value(jps)); + assertEquals(VARCHAR, jdbcType(jps)); + + jps.setObject(1, someInt, Types.FLOAT); + assertEquals(Double.valueOf(someInt), value(jps)); + assertTrue(value(jps) instanceof Double); + assertEquals(FLOAT, jdbcType(jps)); + } + + public void testThrownExceptionsWhenSettingIntegerValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + int someInt = randomInt(); + + SQLException sqle = expectThrows(SQLException.class, () -> jps.setObject(1, someInt, Types.TIMESTAMP)); + assertEquals("Conversion from type [INTEGER] to [Timestamp] not supported", sqle.getMessage()); + + Integer randomIntNotShort = randomIntBetween(32768, Integer.MAX_VALUE); + sqle = expectThrows(SQLException.class, () -> jps.setObject(1, randomIntNotShort, Types.SMALLINT)); + assertEquals("Numeric " + randomIntNotShort + " out of range", sqle.getMessage()); + + sqle = expectThrows(SQLException.class, () -> jps.setObject(1, randomIntNotShort, Types.TINYINT)); + assertEquals("Numeric " + randomIntNotShort + " out of range", sqle.getMessage()); + } + + public void testSettingLongValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + + long someLong = randomLong(); + jps.setLong(1, someLong); + assertEquals(someLong, value(jps)); + assertEquals(BIGINT, jdbcType(jps)); + + jps.setObject(1, someLong); + assertEquals(someLong, value(jps)); + assertEquals(BIGINT, jdbcType(jps)); + assertTrue(value(jps) instanceof Long); + + jps.setObject(1, someLong, Types.VARCHAR); + assertEquals(String.valueOf(someLong), value(jps)); + assertEquals(VARCHAR, jdbcType(jps)); + + jps.setObject(1, someLong, Types.DOUBLE); + assertEquals((double) someLong, value(jps)); + assertEquals(DOUBLE, jdbcType(jps)); + + jps.setObject(1, someLong, Types.FLOAT); + assertEquals((double) someLong, value(jps)); + assertEquals(FLOAT, jdbcType(jps)); + } + + public void testThrownExceptionsWhenSettingLongValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + long someLong = randomLong(); + + SQLException sqle = expectThrows(SQLException.class, () -> jps.setObject(1, someLong, Types.TIMESTAMP)); + assertEquals("Conversion from type [BIGINT] to [Timestamp] not supported", sqle.getMessage()); + + Long randomLongNotShort = randomLongBetween(Integer.MAX_VALUE + 1, Long.MAX_VALUE); + sqle = expectThrows(SQLException.class, () -> jps.setObject(1, randomLongNotShort, Types.INTEGER)); + assertEquals("Numeric " + randomLongNotShort + " out of range", sqle.getMessage()); + + sqle = expectThrows(SQLException.class, () -> jps.setObject(1, randomLongNotShort, Types.SMALLINT)); + assertEquals("Numeric " + randomLongNotShort + " out of range", sqle.getMessage()); + } + + public void testSettingFloatValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + + float someFloat = randomFloat(); + jps.setFloat(1, someFloat); + assertEquals(someFloat, value(jps)); + assertEquals(REAL, jdbcType(jps)); + + jps.setObject(1, someFloat); + assertEquals(someFloat, value(jps)); + assertEquals(REAL, jdbcType(jps)); + assertTrue(value(jps) instanceof Float); + + jps.setObject(1, someFloat, Types.VARCHAR); + assertEquals(String.valueOf(someFloat), value(jps)); + assertEquals(VARCHAR, jdbcType(jps)); + + jps.setObject(1, someFloat, Types.DOUBLE); + assertEquals((double) someFloat, value(jps)); + assertEquals(DOUBLE, jdbcType(jps)); + + jps.setObject(1, someFloat, Types.FLOAT); + assertEquals((double) someFloat, value(jps)); + assertEquals(FLOAT, jdbcType(jps)); + } + + public void testThrownExceptionsWhenSettingFloatValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + float someFloat = randomFloat(); + + SQLException sqle = expectThrows(SQLException.class, () -> jps.setObject(1, someFloat, Types.TIMESTAMP)); + assertEquals("Conversion from type [REAL] to [Timestamp] not supported", sqle.getMessage()); + + Float floatNotInt = 5_155_000_000f; + sqle = expectThrows(SQLException.class, () -> jps.setObject(1, floatNotInt, Types.INTEGER)); + assertEquals(String.format(Locale.ROOT, "Numeric %s out of range", + Long.toString(Math.round(floatNotInt.doubleValue()))), sqle.getMessage()); + + sqle = expectThrows(SQLException.class, () -> jps.setObject(1, floatNotInt, Types.SMALLINT)); + assertEquals(String.format(Locale.ROOT, "Numeric %s out of range", + Long.toString(Math.round(floatNotInt.doubleValue()))), sqle.getMessage()); + } + + public void testSettingDoubleValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + + double someDouble = randomDouble(); + jps.setDouble(1, someDouble); + assertEquals(someDouble, value(jps)); + assertEquals(DOUBLE, jdbcType(jps)); + + jps.setObject(1, someDouble); + assertEquals(someDouble, value(jps)); + assertEquals(DOUBLE, jdbcType(jps)); + assertTrue(value(jps) instanceof Double); + + jps.setObject(1, someDouble, Types.VARCHAR); + assertEquals(String.valueOf(someDouble), value(jps)); + assertEquals(VARCHAR, jdbcType(jps)); + + jps.setObject(1, someDouble, Types.REAL); + assertEquals(new Float(someDouble), value(jps)); + assertEquals(REAL, jdbcType(jps)); + } + + public void testThrownExceptionsWhenSettingDoubleValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + double someDouble = randomDouble(); + + SQLException sqle = expectThrows(SQLException.class, () -> jps.setObject(1, someDouble, Types.TIMESTAMP)); + assertEquals("Conversion from type [DOUBLE] to [Timestamp] not supported", sqle.getMessage()); + + Double doubleNotInt = 5_155_000_000d; + sqle = expectThrows(SQLException.class, () -> jps.setObject(1, doubleNotInt, Types.INTEGER)); + assertEquals(String.format(Locale.ROOT, "Numeric %s out of range", + Long.toString(((Number) doubleNotInt).longValue())), sqle.getMessage()); + } + + public void testUnsupportedClasses() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + SQLFeatureNotSupportedException sfnse = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, new Struct() { + @Override + public String getSQLTypeName() throws SQLException { + return null; + } + @Override + public Object[] getAttributes(Map> map) throws SQLException { + return null; + } + @Override + public Object[] getAttributes() throws SQLException { + return null; + } + })); + assertEquals("Objects of type java.sql.Struct are not supported", sfnse.getMessage()); + + sfnse = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, new URL("http://test"))); + assertEquals("Objects of type java.net.URL are not supported", sfnse.getMessage()); + + sfnse = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setURL(1, new URL("http://test"))); + assertEquals("Objects of type java.net.URL are not supported", sfnse.getMessage()); + + sfnse = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, this, Types.TIMESTAMP)); + assertEquals("Conversion from type " + this.getClass().getName() + " to TIMESTAMP not supported", sfnse.getMessage()); + + SQLException se = expectThrows(SQLException.class, () -> jps.setObject(1, this, 1_000_000)); + assertEquals("Type:1000000 is not a valid Types.java value.", se.getMessage()); + + IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () -> jps.setObject(1, randomShort(), Types.CHAR)); + assertEquals("Unsupported JDBC type [CHAR]", iae.getMessage()); + } + + public void testSettingTimestampValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + + Timestamp someTimestamp = new Timestamp(randomMillisSinceEpoch()); + jps.setTimestamp(1, someTimestamp); + assertEquals(someTimestamp.getTime(), ((Date)value(jps)).getTime()); + assertEquals(TIMESTAMP, jdbcType(jps)); + + Calendar nonDefaultCal = randomCalendar(); + // February 29th, 2016. 01:17:55 GMT = 1456708675000 millis since epoch + jps.setTimestamp(1, new Timestamp(1456708675000L), nonDefaultCal); + assertEquals(1456708675000L, convertFromUTCtoCalendar(((Date)value(jps)), nonDefaultCal)); + assertEquals(TIMESTAMP, jdbcType(jps)); + + long beforeEpochTime = -randomMillisSinceEpoch(); + jps.setTimestamp(1, new Timestamp(beforeEpochTime), nonDefaultCal); + assertEquals(beforeEpochTime, convertFromUTCtoCalendar(((Date)value(jps)), nonDefaultCal)); + assertTrue(value(jps) instanceof java.util.Date); + + jps.setObject(1, someTimestamp, Types.VARCHAR); + assertEquals(someTimestamp.toString(), value(jps).toString()); + assertEquals(VARCHAR, jdbcType(jps)); + } + + public void testThrownExceptionsWhenSettingTimestampValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + Timestamp someTimestamp = new Timestamp(randomMillisSinceEpoch()); + + SQLException sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, someTimestamp, Types.INTEGER)); + assertEquals("Conversion from type java.sql.Timestamp to INTEGER not supported", sqle.getMessage()); + } + + public void testSettingTimeValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + + Time time = new Time(4675000); + Calendar nonDefaultCal = randomCalendar(); + jps.setTime(1, time, nonDefaultCal); + assertEquals(4675000, convertFromUTCtoCalendar(((Date)value(jps)), nonDefaultCal)); + assertEquals(TIMESTAMP, jdbcType(jps)); + assertTrue(value(jps) instanceof java.util.Date); + + jps.setObject(1, time, Types.VARCHAR); + assertEquals(time.toString(), value(jps).toString()); + assertEquals(VARCHAR, jdbcType(jps)); + } + + public void testThrownExceptionsWhenSettingTimeValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + Time time = new Time(4675000); + + SQLException sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, time, Types.INTEGER)); + assertEquals("Conversion from type java.sql.Time to INTEGER not supported", sqle.getMessage()); + } + + public void testSettingSqlDateValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + + java.sql.Date someSqlDate = new java.sql.Date(randomMillisSinceEpoch()); + jps.setDate(1, someSqlDate); + assertEquals(someSqlDate.getTime(), ((Date)value(jps)).getTime()); + assertEquals(TIMESTAMP, jdbcType(jps)); + + someSqlDate = new java.sql.Date(randomMillisSinceEpoch()); + Calendar nonDefaultCal = randomCalendar(); + jps.setDate(1, someSqlDate, nonDefaultCal); + assertEquals(someSqlDate.getTime(), convertFromUTCtoCalendar(((Date)value(jps)), nonDefaultCal)); + assertEquals(TIMESTAMP, jdbcType(jps)); + assertTrue(value(jps) instanceof java.util.Date); + + jps.setObject(1, someSqlDate, Types.VARCHAR); + assertEquals(someSqlDate.toString(), value(jps).toString()); + assertEquals(VARCHAR, jdbcType(jps)); + } + + public void testThrownExceptionsWhenSettingSqlDateValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + java.sql.Date someSqlDate = new java.sql.Date(randomMillisSinceEpoch()); + + SQLException sqle = expectThrows(SQLFeatureNotSupportedException.class, + () -> jps.setObject(1, new java.sql.Date(randomMillisSinceEpoch()), Types.DOUBLE)); + assertEquals("Conversion from type " + someSqlDate.getClass().getName() + " to DOUBLE not supported", sqle.getMessage()); + } + + public void testSettingCalendarValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + Calendar someCalendar = randomCalendar(); + someCalendar.setTimeInMillis(randomMillisSinceEpoch()); + + jps.setObject(1, someCalendar); + assertEquals(someCalendar.getTime(), (Date) value(jps)); + assertEquals(TIMESTAMP, jdbcType(jps)); + assertTrue(value(jps) instanceof java.util.Date); + + jps.setObject(1, someCalendar, Types.VARCHAR); + assertEquals(someCalendar.toString(), value(jps).toString()); + assertEquals(VARCHAR, jdbcType(jps)); + + Calendar nonDefaultCal = randomCalendar(); + jps.setObject(1, nonDefaultCal); + assertEquals(nonDefaultCal.getTime(), (Date) value(jps)); + assertEquals(TIMESTAMP, jdbcType(jps)); + } + + public void testThrownExceptionsWhenSettingCalendarValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + Calendar someCalendar = randomCalendar(); + + SQLException sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, someCalendar, Types.DOUBLE)); + assertEquals("Conversion from type " + someCalendar.getClass().getName() + " to DOUBLE not supported", sqle.getMessage()); + } + + public void testSettingDateValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + Date someDate = new Date(randomMillisSinceEpoch()); + + jps.setObject(1, someDate); + assertEquals(someDate, (Date) value(jps)); + assertEquals(TIMESTAMP, jdbcType(jps)); + assertTrue(value(jps) instanceof java.util.Date); + + jps.setObject(1, someDate, Types.VARCHAR); + assertEquals(someDate.toString(), value(jps).toString()); + assertEquals(VARCHAR, jdbcType(jps)); + } + + public void testThrownExceptionsWhenSettingDateValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + Date someDate = new Date(randomMillisSinceEpoch()); + + SQLException sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, someDate, Types.BIGINT)); + assertEquals("Conversion from type " + someDate.getClass().getName() + " to BIGINT not supported", sqle.getMessage()); + } + + public void testSettingLocalDateTimeValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + LocalDateTime ldt = LocalDateTime.now(Clock.systemDefaultZone()); + + jps.setObject(1, ldt); + assertEquals(Date.class, value(jps).getClass()); + assertEquals(TIMESTAMP, jdbcType(jps)); + assertTrue(value(jps) instanceof java.util.Date); + + jps.setObject(1, ldt, Types.VARCHAR); + assertEquals(ldt.toString(), value(jps).toString()); + assertEquals(VARCHAR, jdbcType(jps)); + } + + public void testThrownExceptionsWhenSettingLocalDateTimeValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + LocalDateTime ldt = LocalDateTime.now(Clock.systemDefaultZone()); + + SQLException sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, ldt, Types.BIGINT)); + assertEquals("Conversion from type " + ldt.getClass().getName() + " to BIGINT not supported", sqle.getMessage()); + } + + public void testSettingByteArrayValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + + byte[] buffer = "some data".getBytes(StandardCharsets.UTF_8); + jps.setBytes(1, buffer); + assertEquals(byte[].class, value(jps).getClass()); + assertEquals(VARBINARY, jdbcType(jps)); + + jps.setObject(1, buffer); + assertEquals(byte[].class, value(jps).getClass()); + assertEquals(VARBINARY, jdbcType(jps)); + assertTrue(value(jps) instanceof byte[]); + + jps.setObject(1, buffer, Types.VARBINARY); + assertEquals((byte[]) value(jps), buffer); + assertEquals(VARBINARY, jdbcType(jps)); + + SQLException sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, buffer, Types.VARCHAR)); + assertEquals("Conversion from type byte[] to VARCHAR not supported", sqle.getMessage()); + + sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, buffer, Types.DOUBLE)); + assertEquals("Conversion from type byte[] to DOUBLE not supported", sqle.getMessage()); + } + + public void testThrownExceptionsWhenSettingByteArrayValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + byte[] buffer = "foo".getBytes(StandardCharsets.UTF_8); + + SQLException sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, buffer, Types.VARCHAR)); + assertEquals("Conversion from type byte[] to VARCHAR not supported", sqle.getMessage()); + + sqle = expectThrows(SQLFeatureNotSupportedException.class, () -> jps.setObject(1, buffer, Types.DOUBLE)); + assertEquals("Conversion from type byte[] to DOUBLE not supported", sqle.getMessage()); + } + + private long randomMillisSinceEpoch() { + return randomLongBetween(0, System.currentTimeMillis()); + } + + private JdbcPreparedStatement createJdbcPreparedStatement() throws SQLException { + return new JdbcPreparedStatement(null, JdbcConfiguration.create("jdbc:es://l:1", null, 0), "?"); + } + + private JDBCType jdbcType(JdbcPreparedStatement jps) throws SQLException { + return jps.query.getParam(1).type; + } + + private Object value(JdbcPreparedStatement jps) throws SQLException { + return jps.query.getParam(1).value; + } + + private Calendar randomCalendar() { + return Calendar.getInstance(randomTimeZone(), Locale.ROOT); + } + + /* + * Converts from UTC to the provided Calendar. + * Helps checking if the converted date/time values using Calendars in set*(...,Calendar) methods did convert + * the values correctly to UTC. + */ + private long convertFromUTCtoCalendar(Date date, Calendar nonDefaultCal) throws SQLException { + return ZonedDateTime.ofInstant(date.toInstant(), ZoneOffset.UTC) + .withZoneSameLocal(nonDefaultCal.getTimeZone().toZoneId()) + .toInstant().toEpochMilli(); + } +} diff --git a/x-pack/plugin/sql/sql-shared-proto/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java b/x-pack/plugin/sql/sql-shared-proto/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java index c024af48187..3f77bc2fc2e 100644 --- a/x-pack/plugin/sql/sql-shared-proto/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java +++ b/x-pack/plugin/sql/sql-shared-proto/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java @@ -61,11 +61,6 @@ public enum DataType { */ public final JDBCType jdbcType; - /** - * Name of corresponding java class - */ - public final String javaName; - /** * Size of the type in bytes *

@@ -105,10 +100,12 @@ public enum DataType { */ public final boolean defaultDocValues; + private final Class javaClass; + DataType(JDBCType jdbcType, Class javaClass, int size, int defaultPrecision, int displaySize, boolean isInteger, boolean isRational, boolean defaultDocValues) { this.esType = name().toLowerCase(Locale.ROOT); - this.javaName = javaClass == null ? null : javaClass.getName(); + this.javaClass = javaClass; this.jdbcType = jdbcType; this.size = size; this.defaultPrecision = defaultPrecision; @@ -125,6 +122,10 @@ public enum DataType { public String sqlName() { return jdbcType.getName(); } + + public Class javaClass() { + return javaClass; + } public boolean isNumeric() { return isInteger || isRational; @@ -152,6 +153,13 @@ public enum DataType { } return jdbcToEs.get(jdbcType); } + + public static Class fromJdbcTypeToJava(JDBCType jdbcType) { + if (jdbcToEs.containsKey(jdbcType) == false) { + throw new IllegalArgumentException("Unsupported JDBC type [" + jdbcType + "]"); + } + return jdbcToEs.get(jdbcType).javaClass(); + } /** * Creates returns DataType enum coresponding to the specified es type