Skip to content

Commit

Permalink
first try at interbase dialect
Browse files Browse the repository at this point in the history
  • Loading branch information
erilong committed May 23, 2011
1 parent b7bffc6 commit ae7e7f1
Show file tree
Hide file tree
Showing 11 changed files with 406 additions and 17 deletions.
Expand Up @@ -36,6 +36,7 @@
import org.jumpmind.symmetric.ddl.platform.hsqldb.HsqlDbPlatform;
import org.jumpmind.symmetric.ddl.platform.hsqldb2.HsqlDb2Platform;
import org.jumpmind.symmetric.ddl.platform.informix.InformixPlatform;
import org.jumpmind.symmetric.ddl.platform.interbase.InterbasePlatform;
import org.jumpmind.symmetric.ddl.platform.mssql.MSSqlPlatform;
import org.jumpmind.symmetric.ddl.platform.mysql.MySqlPlatform;
import org.jumpmind.symmetric.ddl.platform.oracle.Oracle10Platform;
Expand Down Expand Up @@ -123,6 +124,8 @@ public IDbDialect getObject() throws Exception {
dialect = (AbstractDbDialect) beanFactory.getBean("firebirdDialect");
} else if (pf instanceof SybasePlatform) {
dialect = (AbstractDbDialect) beanFactory.getBean("sybaseDialect");
} else if (pf instanceof InterbasePlatform) {
dialect = (AbstractDbDialect) beanFactory.getBean("interbaseDialect");
} else {
throw new DbNotSupportedException();
}
Expand Down
@@ -0,0 +1,178 @@
/*
* Licensed to JumpMind Inc under one or more contributor
* license agreements. See the NOTICE file distributed
* with this work for additional information regarding
* copyright ownership. JumpMind Inc licenses this file
* to you under the GNU Lesser General Public License (the
* "License"); you may not use this file except in compliance
* with the License.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jumpmind.symmetric.db.interbase;

import org.jumpmind.symmetric.db.AbstractDbDialect;
import org.jumpmind.symmetric.db.BinaryEncoding;
import org.jumpmind.symmetric.db.IDbDialect;
import org.jumpmind.symmetric.model.Trigger;
import org.springframework.jdbc.UncategorizedSQLException;
import org.springframework.jdbc.core.JdbcTemplate;

/**
* Database dialect for <a href="http://www.embarcadero.com/products/interbase/">Interbase</a>.
*/

public class InterbaseDbDialect extends AbstractDbDialect implements IDbDialect {

static final String CONTEXT_TABLE_NAME = "context";

static final String CONTEXT_TABLE_CREATE = "create global temporary table %s (id varchar(30), context_value varchar(30)) on commit preserve rows";

static final String CONTEXT_TABLE_INSERT = "insert into %s (id, context_value) values (?, ?)";

static final String SYNC_TRIGGERS_DISABLED_USER_VARIABLE = "sync_triggers_disabled";

static final String SYNC_TRIGGERS_DISABLED_NODE_VARIABLE = "sync_node_disabled";

@Override
protected void initTablesAndFunctionsForSpecificDialect() {
String contextTableName = tablePrefix + "_" + CONTEXT_TABLE_NAME;
try {
jdbcTemplate.queryForInt("select count(*) from " + contextTableName);
} catch (Exception e) {
try {
log.info("GlobalTempTableCreating", contextTableName);
jdbcTemplate.execute(String.format(CONTEXT_TABLE_CREATE, contextTableName));
} catch (Exception ex) {
log.error("InterbaseDialectInitializingError", ex);
}
}
}

@Override
protected void createRequiredFunctions() {
super.createRequiredFunctions();
try {
jdbcTemplate.queryForObject("select sym_escape('') from rdb$database", String.class);
} catch (UncategorizedSQLException e) {
if (e.getSQLException().getErrorCode() == -804) {
log.error("InterbaseSymUdfMissing");
}
throw new RuntimeException("InterbaseSymEscapeMissing", e);
}
}

@Override
protected boolean doesTriggerExistOnPlatform(String catalogName, String schema, String tableName, String triggerName) {
return jdbcTemplate.queryForInt("select count(*) from rdb$triggers where rdb$trigger_name = ?",
new Object[] { triggerName.toUpperCase() }) > 0;
}

public void disableSyncTriggers(JdbcTemplate jdbcTemplate, String nodeId) {
String contextTableName = tablePrefix + "_" + CONTEXT_TABLE_NAME;
jdbcTemplate.update(String.format(CONTEXT_TABLE_INSERT, contextTableName), new Object[] {
SYNC_TRIGGERS_DISABLED_USER_VARIABLE, "1" });
if (nodeId != null) {
jdbcTemplate.update(String.format(CONTEXT_TABLE_INSERT, contextTableName), new Object[] {
SYNC_TRIGGERS_DISABLED_NODE_VARIABLE, nodeId });
}
}

public void enableSyncTriggers(JdbcTemplate jdbcTemplate) {
String contextTableName = tablePrefix + "_" + CONTEXT_TABLE_NAME;
jdbcTemplate.update("delete from " + contextTableName);
}

public String getSyncTriggersExpression() {
return ":" + SYNC_TRIGGERS_DISABLED_USER_VARIABLE + " is null";
}

@Override
public String getTransactionTriggerExpression(String defaultCatalog, String defaultSchema, Trigger trigger) {
return "null";
}

@Override
protected String getTableNamePattern(String tableName) {
/*
* When looking up a table definition, Jaybird treats underscore (_) in
* the table name as a wildcard, so it needs to be escaped, or you'll
* get back column names for more than one table. Example:
* DatabaseMetaData.metaData.getColumns(null, null, "SYM\\_NODE", null)
*/
return tableName.replaceAll("\\_", "\\\\_");
}

@Override
public String getSelectLastInsertIdSql(String sequenceName) {
return "select gen_id(gen_" + sequenceName + ", 0) from rdb$database";
}

@Override
public boolean isBlobSyncSupported() {
return true;
}

@Override
public BinaryEncoding getBinaryEncoding() {
return BinaryEncoding.HEX;
}

public boolean isNonBlankCharColumnSpacePadded() {
return true;
}

public boolean isCharColumnSpaceTrimmed() {
return false;
}

public boolean isEmptyStringNulled() {
return false;
}

@Override
public boolean storesUpperCaseNamesInCatalog() {
return true;
}

@Override
protected boolean allowsNullForIdentityColumn() {
return true;
}

public void purge() {
}

@Override
public String getName() {
return super.getName();
}

public String getDefaultCatalog() {
return null;
}

@Override
public boolean supportsOpenCursorsAcrossCommit() {
return false;
}

@Override
public boolean supportsTransactionId() {
return true;
}

@Override
public void truncateTable(String tableName) {
jdbcTemplate.update("delete from " + tableName);
}
}
@@ -0,0 +1,179 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd" default-lazy-init="true">

<bean id="interbaseDialect" class="org.jumpmind.symmetric.db.interbase.InterbaseDbDialect"
scope="prototype">
<property name="tablePrefix" value="$[sym.sync.table.prefix]" />
<property name="parameterService" ref="parameterService" />
<property name="defaultSchema" value="$[sym.db.default.schema]" />
<property name="streamingResultsFetchSize" value="$[sym.db.jdbc.streaming.results.fetch.size]" />
<property name="sqlTemplate">
<bean class="org.jumpmind.symmetric.db.SqlTemplate">

<property name="functionInstalledSql">
<value>
<![CDATA[select count(*) from rdb$functions where rdb$function_name = upper('$(functionName)')]]>
</value>
</property>
<property name="functionTemplatesToInstall">
<map>
<entry key="escape">
<value>
declare external function $(functionName) cstring(32660)
returns cstring(32660) free_it entry_point 'sym_escape' module_name 'sym_udf'
</value>
</entry>
<entry key="hex">
<value>
declare external function $(functionName) blob
returns cstring(32660) free_it entry_point 'sym_hex' module_name 'sym_udf'
</value>
</entry>
</map>
</property>
<property name="stringColumnTemplate" >
<value>
<![CDATA[case when $(tableAlias).$(columnName) is null then '' else '"' || $[sym.sync.table.prefix]_escape($(tableAlias).$(columnName)) || '"' end ]]>
</value>
</property>
<property name="clobColumnTemplate">
<value>
<![CDATA[case when $(tableAlias).$(columnName) is null then '' else '"' || $[sym.sync.table.prefix]_escape($(tableAlias).$(columnName)) || '"' end ]]>
</value>
</property>
<property name="blobColumnTemplate">
<value>
<![CDATA[case when $(tableAlias).$(columnName) is null then '' else '"' || $[sym.sync.table.prefix]_hex($(tableAlias).$(columnName)) || '"' end ]]>
</value>
</property>
<property name="numberColumnTemplate">
<value>
<![CDATA[case when $(tableAlias).$(columnName) is null then '' else '"' || $(tableAlias).$(columnName) || '"' end ]]>
</value>
</property>
<property name="datetimeColumnTemplate" >
<value>
<![CDATA[case when $(tableAlias).$(columnName) is null then '' else '"' || $(tableAlias).$(columnName) || '"' end ]]>
</value>
</property>
<property name="triggerConcatCharacter" value="||"/>
<property name="newTriggerValue" value="new"/>
<property name="oldTriggerValue" value="old"/>
<property name="sqlTemplates">
<map>
<entry key="insertTriggerTemplate">
<value>
<![CDATA[
create trigger $(triggerName) for $(schemaName)$(tableName) after insert as
declare variable id integer;
declare variable sync_triggers_disabled varchar(30);
declare variable sync_node_disabled varchar(30);
begin
select context_value from $(prefixName)_context where id = 'sync_triggers_disabled' into :sync_triggers_disabled;
if ($(syncOnInsertCondition) and $(syncOnIncomingBatchCondition)) then
begin
select context_value from $(prefixName)_context where id = 'sync_node_disabled' into :sync_node_disabled;
select gen_id($(defaultSchema)gen_$(prefixName)_data_data_id, 1) from rdb$database into :id;
insert into $(defaultSchema)$(prefixName)_data
(data_id, table_name, event_type, trigger_hist_id, row_data, channel_id, transaction_id, source_node_id, external_data, create_time)
values(
:id,
'$(targetTableName)',
'I',
$(triggerHistoryId),
$(columns),
'$(channelName)',
$(txIdExpression),
:sync_node_disabled,
$(externalSelect),
CURRENT_TIMESTAMP
);
end
end
]]>
</value>
</entry>
<entry key="updateTriggerTemplate">
<value>
<![CDATA[
create trigger $(triggerName) for $(schemaName)$(tableName) after update as
declare variable id integer;
declare variable sync_triggers_disabled varchar(30);
declare variable sync_node_disabled varchar(30);
begin
select context_value from $(prefixName)_context where id = 'sync_triggers_disabled' into :sync_triggers_disabled;
if ($(syncOnUpdateCondition) and $(syncOnIncomingBatchCondition)) then
begin
select context_value from $(prefixName)_context where id = 'sync_node_disabled' into :sync_node_disabled;
select gen_id($(defaultSchema)gen_$(prefixName)_data_data_id, 1) from rdb$database into :id;
insert into $(defaultSchema)$(prefixName)_data
(data_id, table_name, event_type, trigger_hist_id, pk_data, row_data, old_data, channel_id, transaction_id, source_node_id, external_data, create_time)
values(
:id,
'$(targetTableName)',
'U',
$(triggerHistoryId),
$(oldKeys),
$(columns),
$(oldColumns),
'$(channelName)',
$(txIdExpression),
:sync_node_disabled,
$(externalSelect),
CURRENT_TIMESTAMP
);
end
end
]]>
</value>
</entry>
<entry key="deleteTriggerTemplate">
<value>
<![CDATA[
create trigger $(triggerName) for $(schemaName)$(tableName) after delete as
declare variable id integer;
declare variable sync_triggers_disabled varchar(30);
declare variable sync_node_disabled varchar(30);
begin
select context_value from $(prefixName)_context where id = 'sync_triggers_disabled' into :sync_triggers_disabled;
if ($(syncOnDeleteCondition) and $(syncOnIncomingBatchCondition)) then
begin
select context_value from $(prefixName)_context where id = 'sync_node_disabled' into :sync_node_disabled;
select gen_id($(defaultSchema)gen_$(prefixName)_data_data_id, 1) from rdb$database into :id;
insert into $(defaultSchema)$(prefixName)_data
(data_id, table_name, event_type, trigger_hist_id, pk_data, old_data, channel_id, transaction_id, source_node_id, external_data, create_time)
values(
:id,
'$(targetTableName)',
'D',
$(triggerHistoryId),
$(oldKeys),
$(oldColumns),
'$(channelName)',
$(txIdExpression),
:sync_node_disabled,
$(externalSelect),
CURRENT_TIMESTAMP
);
end
end
]]>
</value>
</entry>
<entry key="initialLoadSqlTemplate">
<value>
<![CDATA[select $(columns) from $(schemaName)$(tableName) t where $(whereClause)]]>
</value>
</entry>
</map>
</property>
</bean>
</property>
</bean>

</beans>
12 changes: 12 additions & 0 deletions symmetric/symmetric-core/src/main/resources/sql-error-codes.xml
Expand Up @@ -216,4 +216,16 @@
</property>
</bean>

<bean id="Interbase" class="org.springframework.jdbc.support.SQLErrorCodes">
<property name="databaseProductName">
<value>InterBase*</value>
</property>
<property name="useSqlStateForTranslation">
<value>false</value>
</property>
<property name="dataIntegrityViolationCodes">
<value>335544665</value>
</property>
</bean>

</beans>

0 comments on commit ae7e7f1

Please sign in to comment.