Skip to content

Commit

Permalink
[ 1911869 ] Support JDBC Boolean type.
Browse files Browse the repository at this point in the history
hsqldb - reads/writes boolean type
postgres - reads/writes boolean type
mssql - reads bit, writes boolean type
mysql - reads tinyint, writes boolean type
derby -  reads smallint, writes boolean type
oracle - reads/writes number type
  • Loading branch information
erilong committed May 13, 2008
1 parent d302d26 commit 6d3862f
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 43 deletions.
Expand Up @@ -133,6 +133,8 @@ protected String formatAsCsv(Object[] data) {
Binary d = (Binary) object;
b.append(new String(Base64.encodeBase64(d.getBytes())));
b.append("\"");
} else if (object instanceof Boolean) {
b.append(((Boolean) object) ? "1" : "0");
} else {
throw new IllegalStateException("Could not format " + object + " which is of type "
+ object.getClass().getName());
Expand Down
12 changes: 10 additions & 2 deletions symmetric/src/main/java/org/jumpmind/symmetric/db/SqlTemplate.java
Expand Up @@ -63,6 +63,8 @@ public class SqlTemplate {

private String blobColumnTemplate;

private String booleanColumnTemplate;

private String triggerConcatCharacter;

private String newTriggerValue;
Expand Down Expand Up @@ -349,7 +351,6 @@ private String buildColumnString(String origTableAlias, String tableAlias, Colum
for (Column column : columns) {
String templateToUse = null;
switch (column.getTypeCode()) {
case Types.BIT:
case Types.TINYINT:
case Types.SMALLINT:
case Types.INTEGER:
Expand All @@ -359,7 +360,6 @@ private String buildColumnString(String origTableAlias, String tableAlias, Colum
case Types.DOUBLE:
case Types.NUMERIC:
case Types.DECIMAL:
case Types.BOOLEAN:
templateToUse = numberColumnTemplate;
break;
case Types.CHAR:
Expand All @@ -381,6 +381,10 @@ private String buildColumnString(String origTableAlias, String tableAlias, Colum
case Types.TIMESTAMP:
templateToUse = datetimeColumnTemplate;
break;
case Types.BOOLEAN:
case Types.BIT:
templateToUse = booleanColumnTemplate;
break;
case Types.NULL:
case Types.OTHER:
case Types.JAVA_OBJECT:
Expand Down Expand Up @@ -440,6 +444,10 @@ public void setClobColumnTemplate(String clobColumnTemplate) {
this.clobColumnTemplate = clobColumnTemplate;
}

public void setBooleanColumnTemplate(String booleanColumnTemplate) {
this.booleanColumnTemplate = booleanColumnTemplate;
}

public void setTriggerConcatCharacter(String triggerConcatCharacter) {
this.triggerConcatCharacter = triggerConcatCharacter;
}
Expand Down
Expand Up @@ -190,10 +190,12 @@ private int execute(IDataLoaderContext ctx, StatementBuilder st, String[] values
objectValue = new Timestamp(getTime(value, TIMESTAMP_PATTERNS));
} else if (type == Types.CHAR && dbDialect.isCharSpacePadded()) {
objectValue = StringUtils.rightPad(value.toString(), column.getSizeAsInt(), ' ');
} else if (type == Types.INTEGER || type == Types.SMALLINT) {
} else if (type == Types.INTEGER || type == Types.SMALLINT || type == Types.BIT) {
objectValue = Integer.valueOf(value);
} else if (type == Types.NUMERIC || type == Types.DECIMAL) {
objectValue = new BigDecimal(value);
} else if (type == Types.BOOLEAN) {
objectValue = value.equals("1") ? Boolean.TRUE : Boolean.FALSE;
} else if (type == Types.BLOB || type == Types.LONGVARBINARY || type == Types.BINARY) {
if (encoding == BinaryEncoding.NONE) {
objectValue = value.getBytes();
Expand Down
5 changes: 5 additions & 0 deletions symmetric/src/main/resources/dialects/hsqldb.xml
Expand Up @@ -41,6 +41,11 @@
<![CDATA[ case when $(tableAlias).$(columnName) is null then '' else concat(concat('"',to_char($(tableAlias).$(columnName), 'YYYY-MM-DD HH24:MI:SS')),'"') end ||','||]]>
</value>
</property>
<property name="booleanColumnTemplate">
<value>
<![CDATA[case when $(tableAlias).$(columnName) is null then '' when $(tableAlias).$(columnName) then '1' else '0' end||','||]]>
</value>
</property>
<property name="oldTriggerValue" value="t.old_" />
<property name="newTriggerValue" value="t.new_" />
<property name="triggerConcatCharacter" value="||" />
Expand Down
5 changes: 5 additions & 0 deletions symmetric/src/main/resources/dialects/mssql.xml
Expand Up @@ -66,6 +66,11 @@
<![CDATA[case when $(tableAlias).$(columnName) is null then '' else ('"' + convert(varchar,$(tableAlias).$(columnName),121) + '"') end +','+]]>
</value>
</property>
<property name="booleanColumnTemplate">
<value>
<![CDATA[case when $(tableAlias).$(columnName) is null then '' when $(tableAlias).$(columnName) = 1 then '1' else '0' end +','+]]>
</value>
</property>
<property name="triggerConcatCharacter" value="+" />
<property name="newTriggerValue" value="inserted" />
<property name="oldTriggerValue" value="deleted" />
Expand Down
5 changes: 5 additions & 0 deletions symmetric/src/main/resources/dialects/mysql.xml
Expand Up @@ -79,6 +79,11 @@
<value>
<![CDATA[if($(tableAlias).$(columnName) is null,'',concat('"',cast($(tableAlias).$(columnName) as char),'"')),',',]]>
</value>
</property>
<property name="booleanColumnTemplate">
<value>
<![CDATA[coalesce(cast($(tableAlias).$(columnName) as char), ''),',',]]>
</value>
</property>
<property name="triggerConcatCharacter" value=","/>
<property name="newTriggerValue" value="new"/>
Expand Down
5 changes: 5 additions & 0 deletions symmetric/src/main/resources/dialects/postgresql.xml
Expand Up @@ -33,6 +33,11 @@
<value>
<![CDATA[ coalesce(cast($(tableAlias).$(columnName) as varchar), '') ||','||]]>
</value>
</property>
<property name="booleanColumnTemplate">
<value>
<![CDATA[case when $(tableAlias).$(columnName) is null then '' when $(tableAlias).$(columnName) then '1' else '0' end||','||]]>
</value>
</property>
<property name="triggerConcatCharacter" value="||"/>
<property name="newTriggerValue" value="new"/>
Expand Down
Expand Up @@ -20,10 +20,6 @@

package org.jumpmind.symmetric.db;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.List;

Expand All @@ -33,13 +29,16 @@
import org.jumpmind.symmetric.SymmetricEngine;
import org.jumpmind.symmetric.common.Constants;
import org.jumpmind.symmetric.common.TestConstants;
import org.jumpmind.symmetric.db.oracle.OracleDbDialect;
import org.jumpmind.symmetric.model.Node;
import org.jumpmind.symmetric.model.Trigger;
import org.jumpmind.symmetric.service.IBootstrapService;
import org.jumpmind.symmetric.service.IConfigurationService;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.testng.Assert;
import org.testng.annotations.Test;

Expand All @@ -50,20 +49,17 @@ public class DbTriggerTest extends AbstractDatabaseTest {

private static final String TEST_TRIGGERS_TABLE = "test_triggers_table";

final static String INSERT1 = "insert into "
final static String INSERT = "insert into "
+ TEST_TRIGGERS_TABLE
+ " (string_One_Value,string_Two_Value,long_String_Value,time_Value,date_Value,boolean_Value,bigInt_Value,decimal_Value) "
+ "values(?,?,?,?,?,?,?,?)"; //'\\\\','\"','\"1\"',null,null,1,1,1)";

final static Object[] INSERT1_VALUES = new Object[] { "\\\\", "\"", "\"1\"", null, null, 1, 1, 1 };
final static int[] INSERT_TYPES = new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.TIMESTAMP,
Types.DATE, Types.BOOLEAN, Types.INTEGER, Types.DECIMAL };

final static int[] INSERT1_TYPES = new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.TIMESTAMP,
Types.DATE, Types.INTEGER, Types.INTEGER, Types.DECIMAL };
final static Object[] INSERT1_VALUES = new Object[] { "\\\\", "\"", "\"1\"", null, null, Boolean.TRUE, 1, 1 };

final static String INSERT2 = "insert into "
+ TEST_TRIGGERS_TABLE
+ " (string_One_Value,string_Two_Value,long_String_Value,time_Value,date_Value,boolean_Value,bigInt_Value,decimal_Value) "
+ "values('here','here','1',null,null,1,1,1)";
final static Object[] INSERT2_VALUES = new Object[] { "here", "here", "1", null, null, Boolean.TRUE, 1, 1 };

final static String EXPECTED_INSERT1_CSV_ENDSWITH = "\"\\\\\\\\\",\"\\\"\",\"\\\"1\\\"\",,,1,1,1";

Expand Down Expand Up @@ -120,7 +116,7 @@ private int getTriggerHistTableRowCount(SymmetricEngine engine) {
public void validateTestTableTriggers() throws Exception {
JdbcTemplate jdbcTemplate = getJdbcTemplate(getSymmetricEngine());

int count = jdbcTemplate.update(INSERT1, INSERT1_VALUES, INSERT1_TYPES);
int count = jdbcTemplate.update(INSERT, filterValues(INSERT1_VALUES), filterTypes(INSERT_TYPES));

assert count == 1;
String csvString = getNextDataRow(getSymmetricEngine());
Expand All @@ -145,31 +141,25 @@ public void testInitialLoadSql() throws Exception {

@Test(groups = "continuous", dependsOnMethods = "testInitialLoadSql")
public void validateTransactionFunctionailty() throws Exception {
JdbcTemplate jdbcTemplate = getJdbcTemplate(getSymmetricEngine());
jdbcTemplate.execute(new ConnectionCallback() {
public Object doInConnection(Connection c) throws SQLException, DataAccessException {
boolean origValue = c.getAutoCommit();
c.setAutoCommit(false);
Statement stmt = c.createStatement();
stmt.executeUpdate("update " + TEST_TRIGGERS_TABLE + " set time_value=current_timestamp");
stmt.executeUpdate(INSERT2);
c.commit();
c.setAutoCommit(origValue);
ResultSet rs = stmt.executeQuery("select transaction_id from " + TestConstants.TEST_PREFIX
+ "data_event where transaction_id is not null group by transaction_id having count(*)>1");
String batchId = null;
if (rs.next()) {
batchId = rs.getString(1);
}
IDbDialect dbDialect = (IDbDialect) getSymmetricEngine().getApplicationContext().getBean(
Constants.DB_DIALECT);
if (dbDialect.supportsTransactionId()) {
Assert.assertNotNull(batchId);
}
stmt.close();
final JdbcTemplate jdbcTemplate = getJdbcTemplate(getSymmetricEngine());
TransactionTemplate transactionTemplate = new TransactionTemplate(new DataSourceTransactionManager(
getDataSource()));
transactionTemplate.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
jdbcTemplate.update("update " + TEST_TRIGGERS_TABLE + " set time_value=current_timestamp");
jdbcTemplate.update(INSERT, filterValues(INSERT2_VALUES), filterTypes(INSERT_TYPES));
return null;
}
});
String sql = "select transaction_id from " + TestConstants.TEST_PREFIX
+ "data_event where transaction_id is not null group by transaction_id having count(*)>1";
String batchId = (String) jdbcTemplate.queryForObject(sql, String.class);

IDbDialect dbDialect = (IDbDialect) getSymmetricEngine().getApplicationContext().getBean(
Constants.DB_DIALECT);
if (dbDialect.supportsTransactionId()) {
Assert.assertNotNull(batchId);
}
}

@Test(groups = "continuous", dependsOnMethods = "validateTransactionFunctionailty")
Expand All @@ -193,7 +183,7 @@ public void testExcludedColumnsFunctionality() throws Exception {
+ "trigger_hist where trigger_id=" + trigger.getTriggerId() + " and inactive_time is null"), 1,
"We expected only one active record in the trigger_hist table for " + TEST_TRIGGERS_TABLE);

Assert.assertEquals(1, jdbcTemplate.update(INSERT2));
Assert.assertEquals(1, jdbcTemplate.update(INSERT, filterValues(INSERT2_VALUES), filterTypes(INSERT_TYPES)));

String csvString = getNextDataRow(getSymmetricEngine());
boolean match = csvString.endsWith(EXPECTED_INSERT2_CSV_ENDSWITH);
Expand All @@ -204,7 +194,7 @@ public void testExcludedColumnsFunctionality() throws Exception {
public void testDisableTriggers() throws Exception {
JdbcTemplate jdbcTemplate = getJdbcTemplate(getSymmetricEngine());
getDbDialect(getSymmetricEngine()).disableSyncTriggers();
int count = jdbcTemplate.update(INSERT1, INSERT1_VALUES, INSERT1_TYPES);
int count = jdbcTemplate.update(INSERT, filterValues(INSERT1_VALUES), filterTypes(INSERT_TYPES));
getDbDialect(getSymmetricEngine()).enableSyncTriggers();
assert count == 1;
String csvString = getNextDataRow(getSymmetricEngine());
Expand Down Expand Up @@ -235,12 +225,38 @@ public void testTargetTableNameFunctionality() throws Exception {
+ "trigger_hist where trigger_id=" + trigger.getTriggerId() + " and inactive_time is null"), 1,
"We expected only one active record in the trigger_hist table for " + TEST_TRIGGERS_TABLE);

Assert.assertEquals(1, jdbcTemplate.update(INSERT2));
Assert.assertEquals(1, jdbcTemplate.update(INSERT, filterValues(INSERT2_VALUES), filterTypes(INSERT_TYPES)));

String tableName = getNextDataRowTableName(getSymmetricEngine());
Assert.assertEquals(tableName, TARGET_TABLE_NAME, "Received " + tableName + ", Expected " + TARGET_TABLE_NAME);
}

private int[] filterTypes(int[] types) {
boolean isBooleanSupported = ! (getDbDialect() instanceof OracleDbDialect);
int[] filteredTypes = new int[types.length];
for (int i = 0; i < types.length; i++) {
if (types[i] == Types.BOOLEAN && ! isBooleanSupported) {
filteredTypes[i] = Types.INTEGER;
} else {
filteredTypes[i] = types[i];
}
}
return filteredTypes;
}

private Object[] filterValues(Object[] values) {
boolean isBooleanSupported = ! (getDbDialect() instanceof OracleDbDialect);
Object[] filteredValues = new Object[values.length];
for (int i = 0; i < values.length; i++) {
if (values[i] instanceof Boolean && ! isBooleanSupported) {
filteredValues[i] = ((Boolean) values[i]) ? new Integer(1) : new Integer(0);
} else {
filteredValues[i] = values[i];
}
}
return filteredValues;
}

private String getNextDataRow(SymmetricEngine engine) {
JdbcTemplate jdbcTemplate = getJdbcTemplate(engine);
return (String) jdbcTemplate
Expand Down
Expand Up @@ -163,6 +163,8 @@ protected void assertEquals(String[] name, String[] expected, Map<String, Object
} else if (resultObj instanceof Date) {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.0");
resultValue = df.format(resultObj);
} else if (resultObj instanceof Boolean) {
resultValue = ((Boolean) resultObj) ? "1" : "0";
} else if (resultObj != null) {
resultValue = resultObj.toString();
}
Expand Down

0 comments on commit 6d3862f

Please sign in to comment.