IGNITE-9629: JDBC: improved type conversions for thick driver. This closes #4761.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/7b0d30bb Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/7b0d30bb Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/7b0d30bb Branch: refs/heads/ignite-gg-14206 Commit: 7b0d30bb07698e050a2bc2105d6ccf51059a9da9 Parents: 50c5809 Author: tledkov-gridgain <tled...@gridgain.com> Authored: Wed Sep 26 11:17:50 2018 +0300 Committer: devozerov <voze...@gridgain.com> Committed: Wed Sep 26 11:17:50 2018 +0300 ---------------------------------------------------------------------- .../internal/jdbc2/JdbcResultSetSelfTest.java | 272 ++++++++++- .../ignite/jdbc/JdbcErrorsAbstractSelfTest.java | 4 +- .../jdbc/thin/JdbcThinResultSetSelfTest.java | 45 +- .../ignite/internal/jdbc2/JdbcResultSet.java | 487 +++++++++++++++---- 4 files changed, 682 insertions(+), 126 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/7b0d30bb/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSetSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSetSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSetSelfTest.java index 9e60c25..bd73bcd 100644 --- a/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSetSelfTest.java +++ b/modules/clients/src/test/java/org/apache/ignite/internal/jdbc2/JdbcResultSetSelfTest.java @@ -151,7 +151,7 @@ public class JdbcResultSetSelfTest extends GridCommonAbstractTest { o.floatVal = 1.0f; o.doubleVal = 1.0d; o.bigVal = new BigDecimal(1); - o.strVal = "str"; + o.strVal = "1"; o.arrVal = new byte[] {1}; o.dateVal = new Date(1, 1, 1); o.timeVal = new Time(1, 1, 1); @@ -173,12 +173,72 @@ public class JdbcResultSetSelfTest extends GridCommonAbstractTest { if (cnt == 0) { assert rs.getBoolean("boolVal"); assert rs.getBoolean(2); + assert rs.getByte(2) == 1; + assert rs.getInt(2) == 1; + assert rs.getShort(2) == 1; + assert rs.getLong(2) == 1; + assert rs.getDouble(2) == 1.0; + assert rs.getFloat(2) == 1.0f; + assert rs.getBigDecimal(2).equals(new BigDecimal(1)); + assert rs.getString(2).equals("true"); + + assert rs.getObject(2, Boolean.class); + assert rs.getObject(2, Byte.class) == 1; + assert rs.getObject(2, Short.class) == 1; + assert rs.getObject(2, Integer.class) == 1; + assert rs.getObject(2, Long.class) == 1; + assert rs.getObject(2, Float.class) == 1.f; + assert rs.getObject(2, Double.class) == 1; + assert rs.getObject(2, BigDecimal.class).equals(new BigDecimal(1)); + assert rs.getObject(2, String.class).equals("true"); } cnt++; } assert cnt == 1; + + ResultSet rs0 = stmt.executeQuery("select 1"); + + assert rs0.next(); + assert rs0.getBoolean(1); + + rs0 = stmt.executeQuery("select 0"); + + assert rs0.next(); + assert !rs0.getBoolean(1); + + rs0 = stmt.executeQuery("select '1'"); + + assert rs0.next(); + assert rs0.getBoolean(1); + + rs0 = stmt.executeQuery("select '0'"); + + assert rs0.next(); + assert !rs0.getBoolean(1); + + GridTestUtils.assertThrowsAnyCause(log, new Callable<Void>() { + @Override public Void call() throws Exception { + ResultSet rs0 = stmt.executeQuery("select ''"); + + assert rs0.next(); + assert rs0.getBoolean(1); + + return null; + } + }, SQLException.class, "Cannot convert to boolean: "); + + GridTestUtils.assertThrowsAnyCause(log, new Callable<Void>() { + @Override public Void call() throws Exception { + ResultSet rs0 = stmt.executeQuery("select 'qwe'"); + + assert rs0.next(); + assert rs0.getBoolean(1); + + return null; + } + }, SQLException.class, "Cannot convert to boolean: qwe"); } /** @@ -253,6 +313,26 @@ public class JdbcResultSetSelfTest extends GridCommonAbstractTest { if (cnt == 0) { assert rs.getByte("byteVal") == 1; assert rs.getByte(3) == 1; + + assert rs.getBoolean(3); + assert rs.getByte(3) == 1; + assert rs.getInt(3) == 1; + assert rs.getShort(3) == 1; + assert rs.getLong(3) == 1; + assert rs.getDouble(3) == 1.0; + assert rs.getFloat(3) == 1.0f; + assert rs.getBigDecimal(3).equals(new BigDecimal(1)); + assert rs.getString(3).equals("1"); + + assert rs.getObject(3, Boolean.class); + assert rs.getObject(3, Byte.class) == 1; + assert rs.getObject(3, Short.class) == 1; + assert rs.getObject(3, Integer.class) == 1; + assert rs.getObject(3, Long.class) == 1; + assert rs.getObject(3, Float.class) == 1.f; + assert rs.getObject(3, Double.class) == 1; + assert rs.getObject(3, BigDecimal.class).equals(new BigDecimal(1)); + assert rs.getObject(3, String.class).equals("1"); } cnt++; @@ -273,6 +353,26 @@ public class JdbcResultSetSelfTest extends GridCommonAbstractTest { if (cnt == 0) { assert rs.getShort("shortVal") == 1; assert rs.getShort(4) == 1; + + assert rs.getBoolean(4); + assert rs.getByte(4) == 1; + assert rs.getShort(4) == 1; + assert rs.getInt(4) == 1; + assert rs.getLong(4) == 1; + assert rs.getDouble(4) == 1.0; + assert rs.getFloat(4) == 1.0f; + assert rs.getBigDecimal(4).equals(new BigDecimal(1)); + assert rs.getString(4).equals("1"); + + assert rs.getObject(4, Boolean.class); + assert rs.getObject(4, Byte.class) == 1; + assert rs.getObject(4, Short.class) == 1; + assert rs.getObject(4, Integer.class) == 1; + assert rs.getObject(4, Long.class) == 1; + assert rs.getObject(4, Float.class) == 1.f; + assert rs.getObject(4, Double.class) == 1; + assert rs.getObject(4, BigDecimal.class).equals(new BigDecimal(1)); + assert rs.getObject(4, String.class).equals("1"); } cnt++; @@ -293,6 +393,26 @@ public class JdbcResultSetSelfTest extends GridCommonAbstractTest { if (cnt == 0) { assert rs.getInt("intVal") == 1; assert rs.getInt(5) == 1; + + assert rs.getBoolean(5); + assert rs.getByte(5) == 1; + assert rs.getShort(5) == 1; + assert rs.getInt(5) == 1; + assert rs.getLong(5) == 1; + assert rs.getDouble(5) == 1.0; + assert rs.getFloat(5) == 1.0f; + assert rs.getBigDecimal(5).equals(new BigDecimal(1)); + assert rs.getString(5).equals("1"); + + assert rs.getObject(5, Boolean.class); + assert rs.getObject(5, Byte.class) == 1; + assert rs.getObject(5, Short.class) == 1; + assert rs.getObject(5, Integer.class) == 1; + assert rs.getObject(5, Long.class) == 1; + assert rs.getObject(5, Float.class) == 1.f; + assert rs.getObject(5, Double.class) == 1; + assert rs.getObject(5, BigDecimal.class).equals(new BigDecimal(1)); + assert rs.getObject(5, String.class).equals("1"); } cnt++; @@ -313,6 +433,26 @@ public class JdbcResultSetSelfTest extends GridCommonAbstractTest { if (cnt == 0) { assert rs.getLong("longVal") == 1; assert rs.getLong(6) == 1; + + assert rs.getBoolean(6); + assert rs.getByte(6) == 1; + assert rs.getShort(6) == 1; + assert rs.getInt(6) == 1; + assert rs.getLong(6) == 1; + assert rs.getDouble(6) == 1.0; + assert rs.getFloat(6) == 1.0f; + assert rs.getBigDecimal(6).equals(new BigDecimal(1)); + assert rs.getString(6).equals("1"); + + assert rs.getObject(6, Boolean.class); + assert rs.getObject(6, Byte.class) == 1; + assert rs.getObject(6, Short.class) == 1; + assert rs.getObject(6, Integer.class) == 1; + assert rs.getObject(6, Long.class) == 1; + assert rs.getObject(6, Float.class) == 1.f; + assert rs.getObject(6, Double.class) == 1; + assert rs.getObject(6, BigDecimal.class).equals(new BigDecimal(1)); + assert rs.getObject(6, String.class).equals("1"); } cnt++; @@ -333,6 +473,26 @@ public class JdbcResultSetSelfTest extends GridCommonAbstractTest { if (cnt == 0) { assert rs.getFloat("floatVal") == 1.0; assert rs.getFloat(7) == 1.0; + + assert rs.getBoolean(7); + assert rs.getByte(7) == 1; + assert rs.getShort(7) == 1; + assert rs.getInt(7) == 1; + assert rs.getLong(7) == 1; + assert rs.getDouble(7) == 1.0; + assert rs.getFloat(7) == 1.0f; + assert rs.getBigDecimal(7).equals(new BigDecimal(1)); + assert rs.getString(7).equals("1.0"); + + assert rs.getObject(7, Boolean.class); + assert rs.getObject(7, Byte.class) == 1; + assert rs.getObject(7, Short.class) == 1; + assert rs.getObject(7, Integer.class) == 1; + assert rs.getObject(7, Long.class) == 1; + assert rs.getObject(7, Float.class) == 1.f; + assert rs.getObject(7, Double.class) == 1; + assert rs.getObject(7, BigDecimal.class).equals(new BigDecimal(1)); + assert rs.getObject(7, String.class).equals("1.0"); } cnt++; @@ -353,6 +513,26 @@ public class JdbcResultSetSelfTest extends GridCommonAbstractTest { if (cnt == 0) { assert rs.getDouble("doubleVal") == 1.0; assert rs.getDouble(8) == 1.0; + + assert rs.getBoolean(8); + assert rs.getByte(8) == 1; + assert rs.getShort(8) == 1; + assert rs.getInt(8) == 1; + assert rs.getLong(8) == 1; + assert rs.getDouble(8) == 1.0; + assert rs.getFloat(8) == 1.0f; + assert rs.getBigDecimal(8).equals(new BigDecimal(1)); + assert rs.getString(8).equals("1.0"); + + assert rs.getObject(8, Boolean.class); + assert rs.getObject(8, Byte.class) == 1; + assert rs.getObject(8, Short.class) == 1; + assert rs.getObject(8, Integer.class) == 1; + assert rs.getObject(8, Long.class) == 1; + assert rs.getObject(8, Float.class) == 1.f; + assert rs.getObject(8, Double.class) == 1; + assert rs.getObject(8, BigDecimal.class).equals(new BigDecimal(1)); + assert rs.getObject(8, String.class).equals("1.0"); } cnt++; @@ -373,6 +553,26 @@ public class JdbcResultSetSelfTest extends GridCommonAbstractTest { if (cnt == 0) { assert rs.getBigDecimal("bigVal").intValue() == 1; assert rs.getBigDecimal(9).intValue() == 1; + + assert rs.getBoolean(9); + assert rs.getByte(9) == 1; + assert rs.getShort(9) == 1; + assert rs.getInt(9) == 1; + assert rs.getLong(9) == 1; + assert rs.getDouble(9) == 1.0; + assert rs.getFloat(9) == 1.0f; + assert rs.getBigDecimal(9).equals(new BigDecimal(1)); + assert rs.getString(9).equals("1"); + + assert rs.getObject(9, Boolean.class); + assert rs.getObject(9, Byte.class) == 1; + assert rs.getObject(9, Short.class) == 1; + assert rs.getObject(9, Integer.class) == 1; + assert rs.getObject(9, Long.class) == 1; + assert rs.getObject(9, Float.class) == 1.f; + assert rs.getObject(9, Double.class) == 1; + assert rs.getObject(9, BigDecimal.class).equals(new BigDecimal(1)); + assert rs.getObject(9, String.class).equals("1"); } cnt++; @@ -384,6 +584,30 @@ public class JdbcResultSetSelfTest extends GridCommonAbstractTest { /** * @throws Exception If failed. */ + public void testBigDecimalScale() throws Exception { + assert "0.12".equals(convertStringToBigDecimalViaJdbc("0.1234", 2).toString()); + assert "1.001".equals(convertStringToBigDecimalViaJdbc("1.0005", 3).toString()); + assert "1E+3".equals(convertStringToBigDecimalViaJdbc("1205.5", -3).toString()); + assert "1.3E+4".equals(convertStringToBigDecimalViaJdbc("12505.5", -3).toString()); + } + + /** + * @param strDec String representation of a decimal value. + * @param scale Scale. + * @return BigDecimal object. + * @throws SQLException On error. + */ + private BigDecimal convertStringToBigDecimalViaJdbc(String strDec, int scale) throws SQLException { + try(ResultSet rs = stmt.executeQuery("select '" + strDec + "'")) { + assert rs.next(); + + return rs.getBigDecimal(1, scale); + } + } + + /** + * @throws Exception If failed. + */ public void testString() throws Exception { ResultSet rs = stmt.executeQuery(SQL); @@ -391,8 +615,27 @@ public class JdbcResultSetSelfTest extends GridCommonAbstractTest { while (rs.next()) { if (cnt == 0) { - assert "str".equals(rs.getString("strVal")); - assert "str".equals(rs.getString(10)); + assert "1".equals(rs.getString("strVal")); + + assert rs.getBoolean(10); + assert rs.getByte(10) == 1; + assert rs.getShort(10) == 1; + assert rs.getInt(10) == 1; + assert rs.getLong(10) == 1; + assert rs.getDouble(10) == 1.0; + assert rs.getFloat(10) == 1.0f; + assert rs.getBigDecimal(10).equals(new BigDecimal("1")); + assert rs.getString(10).equals("1"); + + assert rs.getObject(10, Boolean.class); + assert rs.getObject(10, Byte.class) == 1; + assert rs.getObject(10, Short.class) == 1; + assert rs.getObject(10, Integer.class) == 1; + assert rs.getObject(10, Long.class) == 1; + assert rs.getObject(10, Float.class) == 1.f; + assert rs.getObject(10, Double.class) == 1; + assert rs.getObject(10, BigDecimal.class).equals(new BigDecimal(1)); + assert rs.getObject(10, String.class).equals("1"); } cnt++; @@ -433,7 +676,14 @@ public class JdbcResultSetSelfTest extends GridCommonAbstractTest { while (rs.next()) { if (cnt == 0) { assert rs.getDate("dateVal").equals(new Date(1, 1, 1)); + assert rs.getDate(12).equals(new Date(1, 1, 1)); + assert rs.getTime(12).equals(new Time(new Date(1, 1, 1).getTime())); + assert rs.getTimestamp(12).equals(new Timestamp(new Date(1, 1, 1).getTime())); + + assert rs.getObject(12, Date.class).equals(new Date(1, 1, 1)); + assert rs.getObject(12, Time.class).equals(new Time(new Date(1, 1, 1).getTime())); + assert rs.getObject(12, Timestamp.class).equals(new Timestamp(new Date(1, 1, 1).getTime())); } cnt++; @@ -454,7 +704,14 @@ public class JdbcResultSetSelfTest extends GridCommonAbstractTest { while (rs.next()) { if (cnt == 0) { assert rs.getTime("timeVal").equals(new Time(1, 1, 1)); + + assert rs.getDate(13).equals(new Date(new Time(1, 1, 1).getTime())); assert rs.getTime(13).equals(new Time(1, 1, 1)); + assert rs.getTimestamp(13).equals(new Timestamp(new Time(1, 1, 1).getTime())); + + assert rs.getObject(13, Date.class).equals(new Date(new Time(1, 1, 1).getTime())); + assert rs.getObject(13, Time.class).equals(new Time(1, 1, 1)); + assert rs.getObject(13, Timestamp.class).equals(new Timestamp(new Time(1, 1, 1).getTime())); } cnt++; @@ -474,7 +731,14 @@ public class JdbcResultSetSelfTest extends GridCommonAbstractTest { while (rs.next()) { if (cnt == 0) { assert rs.getTimestamp("tsVal").getTime() == 1; - assert rs.getTimestamp(14).getTime() == 1; + + assert rs.getDate(14).equals(new Date(new Timestamp(1).getTime())); + assert rs.getTime(14).equals(new Time(new Timestamp(1).getTime())); + assert rs.getTimestamp(14).equals(new Timestamp(1)); + + assert rs.getObject(14, Date.class).equals(new Date(new Timestamp(1).getTime())); + assert rs.getObject(14, Time.class).equals(new Time(new Timestamp(1).getTime())); + assert rs.getObject(14, Timestamp.class).equals(new Timestamp(1)); } cnt++; http://git-wip-us.apache.org/repos/asf/ignite/blob/7b0d30bb/modules/clients/src/test/java/org/apache/ignite/jdbc/JdbcErrorsAbstractSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/JdbcErrorsAbstractSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/JdbcErrorsAbstractSelfTest.java index 59535f8..c44e007 100644 --- a/modules/clients/src/test/java/org/apache/ignite/jdbc/JdbcErrorsAbstractSelfTest.java +++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/JdbcErrorsAbstractSelfTest.java @@ -260,10 +260,10 @@ public abstract class JdbcErrorsAbstractSelfTest extends GridCommonAbstractTest rs.next(); - rs.getLong(1); + rs.getInt(1); } } - }, "0700B", "Cannot convert to long"); + }, "0700B", "Cannot convert to int"); } /** http://git-wip-us.apache.org/repos/asf/ignite/blob/7b0d30bb/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinResultSetSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinResultSetSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinResultSetSelfTest.java index 6b7cdf4..36a0a15 100644 --- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinResultSetSelfTest.java +++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinResultSetSelfTest.java @@ -38,7 +38,6 @@ import java.util.Arrays; import java.util.GregorianCalendar; import java.util.concurrent.Callable; import org.apache.ignite.IgniteCache; -import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.cache.query.annotations.QuerySqlField; import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; @@ -151,7 +150,7 @@ public class JdbcThinResultSetSelfTest extends JdbcThinAbstractSelfTest { o.floatVal = 1.0f; o.doubleVal = 1.0d; o.bigVal = new BigDecimal(1); - o.strVal = "1.0"; + o.strVal = "1"; o.arrVal = new byte[] {1}; o.dateVal = new Date(1, 1, 1); o.timeVal = new Time(1, 1, 1); @@ -548,27 +547,27 @@ public class JdbcThinResultSetSelfTest extends JdbcThinAbstractSelfTest { while (rs.next()) { if (cnt == 0) { - assert "1.0".equals(rs.getString("strVal")); - - assert rs.getBoolean(7); - assert rs.getByte(7) == 1; - assert rs.getShort(7) == 1; - assert rs.getInt(7) == 1; - assert rs.getLong(7) == 1; - assert rs.getDouble(7) == 1.0; - assert rs.getFloat(7) == 1.0f; - assert rs.getBigDecimal(7).equals(new BigDecimal(1)); - assert rs.getString(7).equals("1.0"); - - assert rs.getObject(7, Boolean.class); - assert rs.getObject(7, Byte.class) == 1; - assert rs.getObject(7, Short.class) == 1; - assert rs.getObject(7, Integer.class) == 1; - assert rs.getObject(7, Long.class) == 1; - assert rs.getObject(7, Float.class) == 1.f; - assert rs.getObject(7, Double.class) == 1; - assert rs.getObject(7, BigDecimal.class).equals(new BigDecimal(1)); - assert rs.getObject(7, String.class).equals("1.0"); + assert "1".equals(rs.getString("strVal")); + + assert rs.getBoolean(10); + assert rs.getByte(10) == 1; + assert rs.getShort(10) == 1; + assert rs.getInt(10) == 1; + assert rs.getLong(10) == 1; + assert rs.getDouble(10) == 1.0; + assert rs.getFloat(10) == 1.0f; + assert rs.getBigDecimal(10).equals(new BigDecimal("1")); + assert rs.getString(10).equals("1"); + + assert rs.getObject(10, Boolean.class); + assert rs.getObject(10, Byte.class) == 1; + assert rs.getObject(10, Short.class) == 1; + assert rs.getObject(10, Integer.class) == 1; + assert rs.getObject(10, Long.class) == 1; + assert rs.getObject(10, Float.class) == 1.f; + assert rs.getObject(10, Double.class) == 1; + assert rs.getObject(10, BigDecimal.class).equals(new BigDecimal(1)); + assert rs.getObject(10, String.class).equals("1"); } cnt++; http://git-wip-us.apache.org/repos/asf/ignite/blob/7b0d30bb/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java index e6e8488..2025b90 100755 --- a/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/jdbc2/JdbcResultSet.java @@ -20,6 +20,7 @@ package org.apache.ignite.internal.jdbc2; import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; +import java.net.MalformedURLException; import java.net.URL; import java.sql.Array; import java.sql.Blob; @@ -37,6 +38,9 @@ import java.sql.SQLXML; import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.ParseException; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; @@ -55,6 +59,25 @@ import static org.apache.ignite.internal.jdbc2.JdbcUtils.convertToSqlException; * JDBC result set implementation. */ public class JdbcResultSet implements ResultSet { + /** Decimal format to convert streing to decimal. */ + private static final ThreadLocal<DecimalFormat> decimalFormat = new ThreadLocal<DecimalFormat>() { + /** {@inheritDoc} */ + @Override protected DecimalFormat initialValue() { + DecimalFormatSymbols symbols = new DecimalFormatSymbols(); + + symbols.setGroupingSeparator(','); + symbols.setDecimalSeparator('.'); + + String ptrn = "#,##0.0#"; + + DecimalFormat decimalFormat = new DecimalFormat(ptrn, symbols); + + decimalFormat.setParseBigDecimal(true); + + return decimalFormat; + } + }; + /** Is query. */ private final boolean isQry; @@ -189,7 +212,9 @@ public class JdbcResultSet implements ResultSet { } /** + * Fetch next results page. * + * @throws SQLException On error. */ private void fetchPage() throws SQLException { JdbcConnection conn = (JdbcConnection)stmt.getConnection(); @@ -253,81 +278,277 @@ public class JdbcResultSet implements ResultSet { /** {@inheritDoc} */ @Override public String getString(int colIdx) throws SQLException { - return getTypedValue(colIdx, String.class); + Object val = getValue(colIdx); + + return val == null ? null : String.valueOf(val); } /** {@inheritDoc} */ @Override public boolean getBoolean(int colIdx) throws SQLException { - Boolean val = getTypedValue(colIdx, Boolean.class); + Object val = getValue(colIdx); + + if (val == null) + return false; + + Class<?> cls = val.getClass(); - return val != null ? val : false; + if (cls == Boolean.class) + return ((Boolean)val); + else if (val instanceof Number) + return ((Number)val).intValue() != 0; + else if (cls == String.class || cls == Character.class) { + try { + return Integer.parseInt(val.toString()) != 0; + } + catch (NumberFormatException e) { + throw new SQLException("Cannot convert to boolean: " + val, SqlStateCode.CONVERSION_FAILED, e); + } + } + else + throw new SQLException("Cannot convert to boolean: " + val, SqlStateCode.CONVERSION_FAILED); } /** {@inheritDoc} */ @Override public byte getByte(int colIdx) throws SQLException { - Byte val = getTypedValue(colIdx, Byte.class); + Object val = getValue(colIdx); + + if (val == null) + return 0; - return val != null ? val : 0; + Class<?> cls = val.getClass(); + + if (val instanceof Number) + return ((Number)val).byteValue(); + else if (cls == Boolean.class) + return (Boolean) val ? (byte) 1 : (byte) 0; + else if (cls == String.class || cls == Character.class) { + try { + return Byte.parseByte(val.toString()); + } + catch (NumberFormatException e) { + throw new SQLException("Cannot convert to byte: " + val, SqlStateCode.CONVERSION_FAILED, e); + } + } + else + throw new SQLException("Cannot convert to byte: " + val, SqlStateCode.CONVERSION_FAILED); } /** {@inheritDoc} */ @Override public short getShort(int colIdx) throws SQLException { - Short val = getTypedValue(colIdx, Short.class); + Object val = getValue(colIdx); + + if (val == null) + return 0; + + Class<?> cls = val.getClass(); - return val != null ? val : 0; + if (val instanceof Number) + return ((Number) val).shortValue(); + else if (cls == Boolean.class) + return (Boolean) val ? (short) 1 : (short) 0; + else if (cls == String.class || cls == Character.class) { + try { + return Short.parseShort(val.toString()); + } + catch (NumberFormatException e) { + throw new SQLException("Cannot convert to short: " + val, SqlStateCode.CONVERSION_FAILED, e); + } + } + else + throw new SQLException("Cannot convert to short: " + val, SqlStateCode.CONVERSION_FAILED); } /** {@inheritDoc} */ @Override public int getInt(int colIdx) throws SQLException { - Integer val = getTypedValue(colIdx, Integer.class); + Object val = getValue(colIdx); - return val != null ? val : 0; + if (val == null) + return 0; + + Class<?> cls = val.getClass(); + + if (val instanceof Number) + return ((Number) val).intValue(); + else if (cls == Boolean.class) + return (Boolean) val ? 1 : 0; + else if (cls == String.class || cls == Character.class) { + try { + return Integer.parseInt(val.toString()); + } + catch (NumberFormatException e) { + throw new SQLException("Cannot convert to int: " + val, SqlStateCode.CONVERSION_FAILED, e); + } + } + else + throw new SQLException("Cannot convert to int: " + val, SqlStateCode.CONVERSION_FAILED); } /** {@inheritDoc} */ @Override public long getLong(int colIdx) throws SQLException { - Long val = getTypedValue(colIdx, Long.class); + Object val = getValue(colIdx); + + if (val == null) + return 0; + + Class<?> cls = val.getClass(); - return val != null ? val : 0; + if (val instanceof Number) + return ((Number)val).longValue(); + else if (cls == Boolean.class) + return (long) ((Boolean) val ? 1 : 0); + else if (cls == String.class || cls == Character.class) { + try { + return Long.parseLong(val.toString()); + } + catch (NumberFormatException e) { + throw new SQLException("Cannot convert to long: " + val, SqlStateCode.CONVERSION_FAILED, e); + } + } + else + throw new SQLException("Cannot convert to long: " + val, SqlStateCode.CONVERSION_FAILED); } /** {@inheritDoc} */ @Override public float getFloat(int colIdx) throws SQLException { - Float val = getTypedValue(colIdx, Float.class); + Object val = getValue(colIdx); - return val != null ? val : 0; + if (val == null) + return 0; + + Class<?> cls = val.getClass(); + + if (val instanceof Number) + return ((Number) val).floatValue(); + else if (cls == Boolean.class) + return (float) ((Boolean) val ? 1 : 0); + else if (cls == String.class || cls == Character.class) { + try { + return Float.parseFloat(val.toString()); + } + catch (NumberFormatException e) { + throw new SQLException("Cannot convert to float: " + val, SqlStateCode.CONVERSION_FAILED, e); + } + } + else + throw new SQLException("Cannot convert to float: " + val, SqlStateCode.CONVERSION_FAILED); } /** {@inheritDoc} */ @Override public double getDouble(int colIdx) throws SQLException { - Double val = getTypedValue(colIdx, Double.class); + Object val = getValue(colIdx); + + if (val == null) + return 0; + + Class<?> cls = val.getClass(); - return val != null ? val : 0; + if (val instanceof Number) + return ((Number) val).doubleValue(); + else if (cls == Boolean.class) + return (double)((Boolean) val ? 1 : 0); + else if (cls == String.class || cls == Character.class) { + try { + return Double.parseDouble(val.toString()); + } + catch (NumberFormatException e) { + throw new SQLException("Cannot convert to double: " + val, SqlStateCode.CONVERSION_FAILED, e); + } + } + else + throw new SQLException("Cannot convert to double: " + val, SqlStateCode.CONVERSION_FAILED); } /** {@inheritDoc} */ @Override public BigDecimal getBigDecimal(int colIdx, int scale) throws SQLException { - return getTypedValue(colIdx, BigDecimal.class); + BigDecimal val = getBigDecimal(colIdx); + + return val == null ? null : val.setScale(scale, BigDecimal.ROUND_HALF_UP); } /** {@inheritDoc} */ @Override public byte[] getBytes(int colIdx) throws SQLException { - return getTypedValue(colIdx, byte[].class); + Object val = getValue(colIdx); + + if (val == null) + return null; + + Class<?> cls = val.getClass(); + + if (cls == byte[].class) + return (byte[])val; + else if (cls == Byte.class) + return new byte[] {(byte)val}; + else if (cls == Short.class) { + short x = (short)val; + + return new byte[] {(byte)(x >> 8), (byte)x}; + } + else if (cls == Integer.class) { + int x = (int)val; + + return new byte[] { (byte) (x >> 24), (byte) (x >> 16), (byte) (x >> 8), (byte) x}; + } + else if (cls == Long.class) { + long x = (long)val; + + return new byte[] {(byte) (x >> 56), (byte) (x >> 48), (byte) (x >> 40), (byte) (x >> 32), + (byte) (x >> 24), (byte) (x >> 16), (byte) (x >> 8), (byte) x}; + } + else if (cls == String.class) + return ((String)val).getBytes(); + else + throw new SQLException("Cannot convert to byte[]: " + val, SqlStateCode.CONVERSION_FAILED); } /** {@inheritDoc} */ @Override public Date getDate(int colIdx) throws SQLException { - return getTypedValue(colIdx, Date.class); + Object val = getValue(colIdx); + + if (val == null) + return null; + + Class<?> cls = val.getClass(); + + if (cls == Date.class) + return (Date)val; + else if (cls == java.util.Date.class || cls == Time.class || cls == Timestamp.class) + return new Date(((java.util.Date)val).getTime()); + else + throw new SQLException("Cannot convert to date: " + val, SqlStateCode.CONVERSION_FAILED); } /** {@inheritDoc} */ @Override public Time getTime(int colIdx) throws SQLException { - return getTypedValue(colIdx, Time.class); + Object val = getValue(colIdx); + + if (val == null) + return null; + + Class<?> cls = val.getClass(); + + if (cls == Time.class) + return (Time)val; + else if (cls == java.util.Date.class || cls == Date.class || cls == Timestamp.class) + return new Time(((java.util.Date)val).getTime()); + else + throw new SQLException("Cannot convert to time: " + val, SqlStateCode.CONVERSION_FAILED); } /** {@inheritDoc} */ @Override public Timestamp getTimestamp(int colIdx) throws SQLException { - return getTypedValue(colIdx, Timestamp.class); + Object val = getValue(colIdx); + + if (val == null) + return null; + + Class<?> cls = val.getClass(); + + if (cls == Timestamp.class) + return (Timestamp)val; + else if (cls == java.util.Date.class || cls == Date.class || cls == Time.class) + return new Timestamp(((java.util.Date)val).getTime()); + else + throw new SQLException("Cannot convert to timestamp: " + val, SqlStateCode.CONVERSION_FAILED); } /** {@inheritDoc} */ @@ -353,81 +574,93 @@ public class JdbcResultSet implements ResultSet { /** {@inheritDoc} */ @Override public String getString(String colLb) throws SQLException { - return getTypedValue(colLb, String.class); + int colIdx = findColumn(colLb); + + return getString(colIdx); } /** {@inheritDoc} */ @Override public boolean getBoolean(String colLb) throws SQLException { - Boolean val = getTypedValue(colLb, Boolean.class); + int colIdx = findColumn(colLb); - return val != null ? val : false; + return getBoolean(colIdx); } /** {@inheritDoc} */ @Override public byte getByte(String colLb) throws SQLException { - Byte val = getTypedValue(colLb, Byte.class); + int colIdx = findColumn(colLb); - return val != null ? val : 0; + return getByte(colIdx); } /** {@inheritDoc} */ @Override public short getShort(String colLb) throws SQLException { - Short val = getTypedValue(colLb, Short.class); + int colIdx = findColumn(colLb); - return val != null ? val : 0; + return getShort(colIdx); } /** {@inheritDoc} */ @Override public int getInt(String colLb) throws SQLException { - Integer val = getTypedValue(colLb, Integer.class); + int colIdx = findColumn(colLb); - return val != null ? val : 0; + return getInt(colIdx); } /** {@inheritDoc} */ @Override public long getLong(String colLb) throws SQLException { - Long val = getTypedValue(colLb, Long.class); + int colIdx = findColumn(colLb); - return val != null ? val : 0; + return getLong(colIdx); } /** {@inheritDoc} */ @Override public float getFloat(String colLb) throws SQLException { - Float val = getTypedValue(colLb, Float.class); + int colIdx = findColumn(colLb); - return val != null ? val : 0; + return getFloat(colIdx); } /** {@inheritDoc} */ @Override public double getDouble(String colLb) throws SQLException { - Double val = getTypedValue(colLb, Double.class); + int colIdx = findColumn(colLb); - return val != null ? val : 0; + return getDouble(colIdx); } /** {@inheritDoc} */ @Override public BigDecimal getBigDecimal(String colLb, int scale) throws SQLException { - return getTypedValue(colLb, BigDecimal.class); + int colIdx = findColumn(colLb); + + return getBigDecimal(colIdx, scale); } /** {@inheritDoc} */ @Override public byte[] getBytes(String colLb) throws SQLException { - return getTypedValue(colLb, byte[].class); + int colIdx = findColumn(colLb); + + return getBytes(colIdx); } /** {@inheritDoc} */ @Override public Date getDate(String colLb) throws SQLException { - return getTypedValue(colLb, Date.class); + int colIdx = findColumn(colLb); + + return getDate(colIdx); } /** {@inheritDoc} */ @Override public Time getTime(String colLb) throws SQLException { - return getTypedValue(colLb, Time.class); + int colIdx = findColumn(colLb); + + return getTime(colIdx); } /** {@inheritDoc} */ @Override public Timestamp getTimestamp(String colLb) throws SQLException { - return getTypedValue(colLb, Timestamp.class); + int colIdx = findColumn(colLb); + + return getTimestamp(colIdx); } /** {@inheritDoc} */ @@ -482,12 +715,14 @@ public class JdbcResultSet implements ResultSet { /** {@inheritDoc} */ @Override public Object getObject(int colIdx) throws SQLException { - return getTypedValue(colIdx, Object.class); + return getValue(colIdx); } /** {@inheritDoc} */ @Override public Object getObject(String colLb) throws SQLException { - return getTypedValue(colLb, Object.class); + int colIdx = findColumn(colLb); + + return getValue(colIdx); } /** {@inheritDoc} */ @@ -499,6 +734,8 @@ public class JdbcResultSet implements ResultSet { if (idx == -1) throw new SQLException("Column not found: " + colLb); + assert idx >= 0; + return idx + 1; } @@ -518,12 +755,36 @@ public class JdbcResultSet implements ResultSet { /** {@inheritDoc} */ @Override public BigDecimal getBigDecimal(int colIdx) throws SQLException { - return getTypedValue(colIdx, BigDecimal.class); + Object val = getValue(colIdx); + + if (val == null) + return null; + + Class<?> cls = val.getClass(); + + if (cls == BigDecimal.class) + return (BigDecimal)val; + else if (val instanceof Number) + return new BigDecimal(((Number)val).doubleValue()); + else if (cls == Boolean.class) + return new BigDecimal((Boolean)val ? 1 : 0); + else if (cls == String.class || cls == Character.class) { + try { + return (BigDecimal)decimalFormat.get().parse(val.toString()); + } + catch (ParseException e) { + throw new SQLException("Cannot convert to BigDecimal: " + val, SqlStateCode.CONVERSION_FAILED, e); + } + } + else + throw new SQLException("Cannot convert to BigDecimal: " + val, SqlStateCode.CONVERSION_FAILED); } /** {@inheritDoc} */ @Override public BigDecimal getBigDecimal(String colLb) throws SQLException { - return getTypedValue(colLb, BigDecimal.class); + int colIdx = findColumn(colLb); + + return getBigDecimal(colIdx); } /** {@inheritDoc} */ @@ -997,7 +1258,7 @@ public class JdbcResultSet implements ResultSet { /** {@inheritDoc} */ @Override public Object getObject(int colIdx, Map<String, Class<?>> map) throws SQLException { - return getTypedValue(colIdx, Object.class); + throw new SQLFeatureNotSupportedException("SQL structured type are not supported."); } /** {@inheritDoc} */ @@ -1028,7 +1289,7 @@ public class JdbcResultSet implements ResultSet { /** {@inheritDoc} */ @Override public Object getObject(String colLb, Map<String, Class<?>> map) throws SQLException { - return getTypedValue(colLb, Object.class); + throw new SQLFeatureNotSupportedException("SQL structured type are not supported."); } /** {@inheritDoc} */ @@ -1059,42 +1320,62 @@ public class JdbcResultSet implements ResultSet { /** {@inheritDoc} */ @Override public Date getDate(int colIdx, Calendar cal) throws SQLException { - return getTypedValue(colIdx, Date.class); + return getDate(colIdx); } /** {@inheritDoc} */ @Override public Date getDate(String colLb, Calendar cal) throws SQLException { - return getTypedValue(colLb, Date.class); + return getDate(colLb); } /** {@inheritDoc} */ @Override public Time getTime(int colIdx, Calendar cal) throws SQLException { - return getTypedValue(colIdx, Time.class); + return getTime(colIdx); } /** {@inheritDoc} */ @Override public Time getTime(String colLb, Calendar cal) throws SQLException { - return getTypedValue(colLb, Time.class); + return getTime(colLb); } /** {@inheritDoc} */ @Override public Timestamp getTimestamp(int colIdx, Calendar cal) throws SQLException { - return getTypedValue(colIdx, Timestamp.class); + return getTimestamp(colIdx); } /** {@inheritDoc} */ @Override public Timestamp getTimestamp(String colLb, Calendar cal) throws SQLException { - return getTypedValue(colLb, Timestamp.class); + return getTimestamp(colLb); } /** {@inheritDoc} */ @Override public URL getURL(int colIdx) throws SQLException { - return getTypedValue(colIdx, URL.class); + Object val = getValue(colIdx); + + if (val == null) + return null; + + Class<?> cls = val.getClass(); + + if (cls == URL.class) + return (URL)val; + else if (cls == String.class) { + try { + return new URL(val.toString()); + } + catch (MalformedURLException e) { + throw new SQLException("Cannot convert to URL: " + val, SqlStateCode.CONVERSION_FAILED, e); + } + } + else + throw new SQLException("Cannot convert to URL: " + val, SqlStateCode.CONVERSION_FAILED); } /** {@inheritDoc} */ @Override public URL getURL(String colLb) throws SQLException { - return getTypedValue(colLb, URL.class); + int colIdx = findColumn(colLb); + + return getURL(colIdx); } /** {@inheritDoc} */ @@ -1486,57 +1767,77 @@ public class JdbcResultSet implements ResultSet { } /** {@inheritDoc} */ - @Override public <T> T getObject(int colIdx, Class<T> type) throws SQLException { - return getTypedValue(colIdx, type); + @Override public <T> T getObject(int colIdx, Class<T> targetCls) throws SQLException { + return (T)getObject0(colIdx, targetCls); } /** {@inheritDoc} */ - @Override public <T> T getObject(String colLb, Class<T> type) throws SQLException { - return getTypedValue(colLb, type); + @Override public <T> T getObject(String colLb, Class<T> targetCls) throws SQLException { + int colIdx = findColumn(colLb); + + return getObject(colIdx, targetCls); } /** - * Gets casted field value by label. - * - * @param colLb Column label. - * @param cls Value class. - * @return Casted field value. - * @throws SQLException In case of error. + * @param colIdx Column index. + * @param targetCls Class representing the Java data type to convert the designated column to. + * @return Converted object. + * @throws SQLException On error. */ - private <T> T getTypedValue(String colLb, Class<T> cls) throws SQLException { - ensureNotClosed(); - ensureHasCurrentRow(); - - String name = colLb.toUpperCase(); - - Integer idx = stmt.fieldsIdxs.get(name); - - int colIdx; - - if (idx != null) - colIdx = idx; + private Object getObject0(int colIdx, Class<?> targetCls) throws SQLException { + if (targetCls == Boolean.class) + return getBoolean(colIdx); + else if (targetCls == Byte.class) + return getByte(colIdx); + else if (targetCls == Short.class) + return getShort(colIdx); + else if (targetCls == Integer.class) + return getInt(colIdx); + else if (targetCls == Long.class) + return getLong(colIdx); + else if (targetCls == Float.class) + return getFloat(colIdx); + else if (targetCls == Double.class) + return getDouble(colIdx); + else if (targetCls == String.class) + return getString(colIdx); + else if (targetCls == BigDecimal.class) + return getBigDecimal(colIdx); + else if (targetCls == Date.class) + return getDate(colIdx); + else if (targetCls == Time.class) + return getTime(colIdx); + else if (targetCls == Timestamp.class) + return getTimestamp(colIdx); + else if (targetCls == byte[].class) + return getBytes(colIdx); + else if (targetCls == URL.class) + return getURL(colIdx); else { - colIdx = cols.indexOf(name) + 1; + Object val = getValue(colIdx); - if (colIdx <= 0) - throw new SQLException("Invalid column label: " + colLb); + if (val == null) + return null; - stmt.fieldsIdxs.put(name, colIdx); - } + Class<?> cls = val.getClass(); - return getTypedValue(colIdx, cls); + if (targetCls == cls) + return val; + else + throw new SQLException("Cannot convert to " + targetCls.getName() + ": " + val, + SqlStateCode.CONVERSION_FAILED); + } } /** - * Gets casted field value by index. + * Gets object field value by index. * * @param colIdx Column index. - * @param cls Value class. - * @return Casted field value. + * @return Object field value. * @throws SQLException In case of error. */ @SuppressWarnings("unchecked") - private <T> T getTypedValue(int colIdx, Class<T> cls) throws SQLException { + private Object getValue(int colIdx) throws SQLException { ensureNotClosed(); ensureHasCurrentRow(); @@ -1545,18 +1846,10 @@ public class JdbcResultSet implements ResultSet { wasNull = val == null; - if (val == null) - return null; - else if (cls == String.class) - return (T)String.valueOf(val); - else - return cls.cast(val); - } - catch (IndexOutOfBoundsException ignored) { - throw new SQLException("Invalid column index: " + colIdx); + return val; } - catch (ClassCastException ignored) { - throw new SQLException("Cannot convert to " + cls.getSimpleName().toLowerCase(), SqlStateCode.CONVERSION_FAILED); + catch (IndexOutOfBoundsException e) { + throw new SQLException("Invalid column index: " + colIdx, e); } }