From 714b1575b26d49fadea35004168ae58764c90935 Mon Sep 17 00:00:00 2001 From: Austin Brougher Date: Tue, 13 Aug 2013 17:01:44 +0000 Subject: [PATCH] 0001334: The issue with jConnect JDBC, SQL Anywhere and decimal value. Fixing unit test errors on ASE and mapping issues on ASA. --- .../db/platform/ase/AseDatabasePlatform.java | 4 +- .../db/platform/ase/AseJdbcSqlTemplate.java | 68 ++++++++++++++++++- .../SqlAnywhereDatabasePlatform.java | 3 +- .../sqlanywhere/SqlAnywhereDdlReader.java | 5 -- .../SqlAnywhereJdbcSqlTemplate.java | 65 +++++++++++++++++- .../org/jumpmind/db/sql/JdbcSqlTemplate.java | 6 ++ .../src/test/resources/db-test.properties | 24 +++++-- 7 files changed, 157 insertions(+), 18 deletions(-) diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/ase/AseDatabasePlatform.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/ase/AseDatabasePlatform.java index 897ea448b8..3907ca04a8 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/ase/AseDatabasePlatform.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/ase/AseDatabasePlatform.java @@ -27,7 +27,7 @@ import org.apache.commons.lang.StringUtils; import org.jumpmind.db.platform.AbstractJdbcDatabasePlatform; import org.jumpmind.db.platform.DatabaseNamesConstants; -import org.jumpmind.db.platform.ase.AseDdlBuilder; +import org.jumpmind.db.sql.JdbcUtils; import org.jumpmind.db.sql.SqlTemplateSettings; /* @@ -68,7 +68,7 @@ protected AseDdlReader createDdlReader() { @Override protected AseJdbcSqlTemplate createSqlTemplate() { - return new AseJdbcSqlTemplate(dataSource, settings, null, getDatabaseInfo()); + return new AseJdbcSqlTemplate(dataSource, settings, null, getDatabaseInfo(), JdbcUtils.getNativeJdbcExtractory()); } public String getName() { diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/ase/AseJdbcSqlTemplate.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/ase/AseJdbcSqlTemplate.java index c4361a9cd7..04566f4f8f 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/ase/AseJdbcSqlTemplate.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/ase/AseJdbcSqlTemplate.java @@ -20,19 +20,35 @@ */ package org.jumpmind.db.platform.ase; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.sql.PreparedStatement; +import java.sql.SQLException; + import javax.sql.DataSource; import org.jumpmind.db.platform.DatabaseInfo; +import org.jumpmind.db.platform.sqlanywhere.SqlAnywhereJdbcSqlTemplate; +import org.jumpmind.db.sql.ISqlTemplate; import org.jumpmind.db.sql.ISqlTransaction; import org.jumpmind.db.sql.JdbcSqlTemplate; import org.jumpmind.db.sql.SqlTemplateSettings; import org.jumpmind.db.sql.SymmetricLobHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.SqlTypeValue; +import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor; + +public class AseJdbcSqlTemplate extends JdbcSqlTemplate implements ISqlTemplate { + + static final Logger log = LoggerFactory.getLogger(AseJdbcSqlTemplate.class); -public class AseJdbcSqlTemplate extends JdbcSqlTemplate { + private NativeJdbcExtractor nativeJdbcExtractor; public AseJdbcSqlTemplate(DataSource dataSource, SqlTemplateSettings settings, - SymmetricLobHandler lobHandler, DatabaseInfo databaseInfo) { + SymmetricLobHandler lobHandler, DatabaseInfo databaseInfo, NativeJdbcExtractor nativeJdbcExtractor) { super(dataSource, settings, lobHandler, databaseInfo); + this.nativeJdbcExtractor = nativeJdbcExtractor; primaryKeyViolationCodes = new int[] {423,511,515,530,547,2601,2615,2714}; } @@ -46,4 +62,52 @@ protected boolean allowsNullForIdentityColumn() { return false; } + protected void setDecimalValue(PreparedStatement ps, int i, Object arg, int argType) + throws SQLException { + PreparedStatement nativeStatement = getNativeStmt(ps); + if (nativeStatement != null + && "com.sybase.jdbc4.jdbc.SybPreparedStatement".equals(nativeStatement.getClass() + .getName())) { + Class clazz = nativeStatement.getClass(); + Class[] parameterTypes = new Class[] { int.class, BigDecimal.class, int.class, + int.class }; + Method method = null; + try { + method = clazz.getMethod("setBigDecimal", parameterTypes); + BigDecimal value = (BigDecimal) arg; + Object[] params = new Object[] { new Integer(i), value, + new Integer(value.precision()), new Integer(value.scale()) }; + method.invoke(nativeStatement, params); + } catch (Exception e) { + log.info("Can't find stmt.setBigDecimal(int,BigDecimal,int,int) method: " + + e.getMessage()); + return; + } + } + } + + private PreparedStatement getNativeStmt(PreparedStatement ps) { + PreparedStatement stmt = ps; + try { + stmt = nativeJdbcExtractor.getNativePreparedStatement(ps); + } catch (SQLException ex) { + log.debug("Could not find a native preparedstatement using {}", nativeJdbcExtractor + .getClass().getName()); + } + return stmt; + } + + @Override + public void setValues(PreparedStatement ps, Object[] args) + throws SQLException { + super.setValues(ps, args); + if (args != null && args.length > 0) { + int[] argTypes = new int[args.length]; + for (int i = 0; i < argTypes.length; i++) { + argTypes[i] = SqlTypeValue.TYPE_UNKNOWN; + } + setValues(ps, args, argTypes, getLobHandler().getDefaultHandler()); + } + } + } diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/sqlanywhere/SqlAnywhereDatabasePlatform.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/sqlanywhere/SqlAnywhereDatabasePlatform.java index 283d346470..6edf12b02b 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/sqlanywhere/SqlAnywhereDatabasePlatform.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/sqlanywhere/SqlAnywhereDatabasePlatform.java @@ -27,6 +27,7 @@ import org.apache.commons.lang.StringUtils; import org.jumpmind.db.platform.AbstractJdbcDatabasePlatform; import org.jumpmind.db.platform.DatabaseNamesConstants; +import org.jumpmind.db.sql.JdbcUtils; import org.jumpmind.db.sql.SqlTemplateSettings; /* @@ -67,7 +68,7 @@ protected SqlAnywhereDdlReader createDdlReader() { @Override protected SqlAnywhereJdbcSqlTemplate createSqlTemplate() { - return new SqlAnywhereJdbcSqlTemplate(dataSource, settings, null, getDatabaseInfo()); + return new SqlAnywhereJdbcSqlTemplate(dataSource, settings, null, getDatabaseInfo(), JdbcUtils.getNativeJdbcExtractory()); } public String getName() { diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/sqlanywhere/SqlAnywhereDdlReader.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/sqlanywhere/SqlAnywhereDdlReader.java index 326da75b59..29cdaa9204 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/sqlanywhere/SqlAnywhereDdlReader.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/sqlanywhere/SqlAnywhereDdlReader.java @@ -88,11 +88,6 @@ protected Column readColumn(DatabaseMetaDataWrapper metaData, Map && (column.getScale() == 0)) { // Back-mapping to BIGINT column.setMappedTypeCode(Types.BIGINT); - } else if (column.getMappedTypeCode() == Types.NUMERIC - || column.getMappedTypeCode() == Types.DECIMAL) { - // ASA truncates everything to the right of the decimal point unless - // we map to DOUBLE. - column.setMappedTypeCode(Types.DOUBLE); } else if (column.getDefaultValue() != null) { if (column.getMappedTypeCode() == Types.TIMESTAMP) { // Sybase maintains the default values for DATE/TIME jdbc types, diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/sqlanywhere/SqlAnywhereJdbcSqlTemplate.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/sqlanywhere/SqlAnywhereJdbcSqlTemplate.java index 66dc33f1cc..5fd6a41afe 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/sqlanywhere/SqlAnywhereJdbcSqlTemplate.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/sqlanywhere/SqlAnywhereJdbcSqlTemplate.java @@ -20,6 +20,11 @@ */ package org.jumpmind.db.platform.sqlanywhere; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.sql.PreparedStatement; +import java.sql.SQLException; + import javax.sql.DataSource; import org.jumpmind.db.platform.DatabaseInfo; @@ -27,12 +32,21 @@ import org.jumpmind.db.sql.JdbcSqlTemplate; import org.jumpmind.db.sql.SqlTemplateSettings; import org.jumpmind.db.sql.SymmetricLobHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.SqlTypeValue; +import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor; public class SqlAnywhereJdbcSqlTemplate extends JdbcSqlTemplate implements ISqlTemplate { + static final Logger log = LoggerFactory.getLogger(SqlAnywhereJdbcSqlTemplate.class); + + private NativeJdbcExtractor nativeJdbcExtractor; + public SqlAnywhereJdbcSqlTemplate(DataSource dataSource, SqlTemplateSettings settings, - SymmetricLobHandler lobHandler, DatabaseInfo databaseInfo) { + SymmetricLobHandler lobHandler, DatabaseInfo databaseInfo, NativeJdbcExtractor nativeJdbcExtractor) { super(dataSource, settings, lobHandler, databaseInfo); + this.nativeJdbcExtractor = nativeJdbcExtractor; primaryKeyViolationCodes = new int[] {423,511,515,530,547,2601,2615,2714}; } @@ -41,5 +55,54 @@ protected boolean allowsNullForIdentityColumn() { return false; } + protected void setDecimalValue(PreparedStatement ps, int i, Object arg, int argType) + throws SQLException { + PreparedStatement nativeStatement = getNativeStmt(ps); + if (nativeStatement != null + && "com.sybase.jdbc4.jdbc.SybPreparedStatement".equals(nativeStatement.getClass() + .getName())) { + Class clazz = nativeStatement.getClass(); + Class[] parameterTypes = new Class[] { int.class, BigDecimal.class, int.class, + int.class }; + Method method = null; + + try { + method = clazz.getMethod("setBigDecimal", parameterTypes); + BigDecimal value = (BigDecimal) arg; + Object[] params = new Object[] { new Integer(i), value, + new Integer(value.precision()), + new Integer(value.scale()) }; + method.invoke(nativeStatement, params); + } catch (Exception e) { + log.info("Can't find stmt.setBigDecimal(int,BigDecimal,int,int) method: " + + e.getMessage()); + return; + } + } + } + + private PreparedStatement getNativeStmt(PreparedStatement ps) { + PreparedStatement stmt = ps; + try { + stmt = nativeJdbcExtractor.getNativePreparedStatement(ps); + } catch (SQLException ex) { + log.debug("Could not find a native preparedstatement using {}", nativeJdbcExtractor + .getClass().getName()); + } + return stmt; + } + + @Override + public void setValues(PreparedStatement ps, Object[] args) + throws SQLException { + super.setValues(ps, args); + if (args != null && args.length > 0) { + int[] argTypes = new int[args.length]; + for (int i = 0; i < argTypes.length; i++) { + argTypes[i] = SqlTypeValue.TYPE_UNKNOWN; + } + setValues(ps, args, argTypes, getLobHandler().getDefaultHandler()); + } + } } diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/sql/JdbcSqlTemplate.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/sql/JdbcSqlTemplate.java index 104d4b73f4..50c4cc3672 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/sql/JdbcSqlTemplate.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/sql/JdbcSqlTemplate.java @@ -875,12 +875,18 @@ public void setValues(PreparedStatement ps, Object[] args, int[] argTypes, lobHandler.getLobCreator().setBlobAsBytes(ps, i, arg.toString().getBytes()); } else if (argType == Types.CLOB && lobHandler != null) { lobHandler.getLobCreator().setClobAsString(ps, i, (String) arg); + } else if (argType == Types.DECIMAL && arg != null) { + setDecimalValue(ps, i, arg, argType); } else { StatementCreatorUtils.setParameterValue(ps, i, verifyArgType(arg, argType), arg); } } } + protected void setDecimalValue(PreparedStatement ps, int i, Object arg, int argType) throws SQLException { + StatementCreatorUtils.setParameterValue(ps, i, verifyArgType(arg, argType), arg); + } + protected int verifyArgType(Object arg, int argType) { if (argType == -101 || argType == Types.OTHER) { return SqlTypeValue.TYPE_UNKNOWN; diff --git a/symmetric-jdbc/src/test/resources/db-test.properties b/symmetric-jdbc/src/test/resources/db-test.properties index a028f96bc9..68a4d9fabb 100644 --- a/symmetric-jdbc/src/test/resources/db-test.properties +++ b/symmetric-jdbc/src/test/resources/db-test.properties @@ -18,10 +18,20 @@ # specific language governing permissions and limitations # under the License. # +# +#test.server=h2 +#test.root=h2 +#test.client=h2 + +test.server=sqlanywhere +test.root=sqlanywhere +test.client=sqlanywhere + +#test.server=ase +#test.root=ase +#test.client=ase + -test.server=h2 -test.root=h2 -test.client=h2 mysql.db.driver=com.mysql.jdbc.Driver mysql.db.user=root @@ -132,10 +142,10 @@ informix.client.db.url=jdbc:informix-sqli://dbdev1:30277/symmetricclient:INFORMI ase.db.driver=com.sybase.jdbc4.jdbc.SybDriver ase.db.user=sa -ase.db.password=admin123 -ase.root.db.url=jdbc:sybase:Tds:dbdev1:5000/symmetricroot -ase.server.db.url=jdbc:sybase:Tds:dbdev1:5000/symmetricroot -ase.client.db.url=jdbc:sybase:Tds:dbdev1:5000/symmetricclient +ase.db.password= +ase.root.db.url=jdbc:sybase:Tds:localhost:7100/symmetricroot +ase.server.db.url=jdbc:sybase:Tds:localhost:7100/symmetricroot +ase.client.db.url=jdbc:sybase:Tds:localhost:7100/symmetricclient ase12.db.driver=com.sybase.jdbc4.jdbc.SybDriver ase12.db.user=sa