Skip to content

Commit

Permalink
0002626: Set order of triggers to first on SQL Server to preserve order
Browse files Browse the repository at this point in the history
of changes
  • Loading branch information
erilong committed Jun 3, 2016
1 parent 37b7541 commit e663e24
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 14 deletions.
Expand Up @@ -26,6 +26,7 @@
import java.sql.SQLException;
import java.sql.Statement;

import org.apache.commons.lang.StringUtils;
import org.jumpmind.db.model.Column;
import org.jumpmind.db.model.Database;
import org.jumpmind.db.model.Table;
Expand All @@ -43,7 +44,10 @@
import org.jumpmind.symmetric.common.TableConstants;
import org.jumpmind.symmetric.db.AbstractSymmetricDialect;
import org.jumpmind.symmetric.db.ISymmetricDialect;
import org.jumpmind.symmetric.io.data.DataEventType;
import org.jumpmind.symmetric.model.Channel;
import org.jumpmind.symmetric.model.Trigger;
import org.jumpmind.symmetric.model.TriggerHistory;
import org.jumpmind.symmetric.service.IParameterService;

/*
Expand Down Expand Up @@ -290,6 +294,39 @@ protected String switchCatalogForTriggerInstall(String catalog, ISqlTransaction
}
}

@Override
protected void postCreateTrigger(ISqlTransaction transaction, StringBuilder sqlBuffer, DataEventType dml,
Trigger trigger, TriggerHistory hist, Channel channel, String tablePrefix, Table table) {
if (parameterService.is(ParameterConstants.MSSQL_TRIGGER_ORDER_FIRST, false)) {
String schemaName = "";
if (StringUtils.isNotBlank(trigger.getSourceSchemaName())) {
schemaName = trigger.getSourceSchemaName() + ".";
}

String triggerNameFirst = (String) transaction.queryForObject(
"select tr.name " +
"from sys.triggers tr inner join sys.trigger_events te on te.object_id = tr.object_id " +
"inner join sys.tables t on t.object_id = tr.parent_id " +
"where t.name = ? and te.type_desc = ? and te.is_first = 1", String.class, table.getName(), dml.name());
if (StringUtils.isNotBlank(triggerNameFirst)) {
log.warn("Existing first trigger '{}{}' is being set to order of 'None'", schemaName, triggerNameFirst);
transaction.execute("exec sys.sp_settriggerorder @triggername = '" + schemaName +
triggerNameFirst + "', @order = 'None', @stmttype = '" + dml.name() + "'");
}

String triggerName = null;
if (dml.equals(DataEventType.INSERT)) {
triggerName = hist.getNameForInsertTrigger();
} else if (dml.equals(DataEventType.UPDATE)) {
triggerName = hist.getNameForUpdateTrigger();
} else {
triggerName = hist.getNameForDeleteTrigger();
}
transaction.execute("exec sys.sp_settriggerorder @triggername = '" + schemaName +
triggerName + "', @order = 'First', @stmttype = '" + dml.name() + "'");
}
}

@Override
public BinaryEncoding getBinaryEncoding() {
return BinaryEncoding.BASE64;
Expand Down
Expand Up @@ -306,6 +306,8 @@ private ParameterConstants() {

public final static String MSSQL_TRIGGER_EXECUTE_AS = "mssql.trigger.execute.as";

public final static String MSSQL_TRIGGER_ORDER_FIRST = "mssql.trigger.order.first";

public final static String SQLITE_TRIGGER_FUNCTION_TO_USE = "sqlite.trigger.function.to.use";

public final static String AS400_CAST_CLOB_TO = "as400.cast.clob.to";
Expand Down
Expand Up @@ -324,9 +324,6 @@ public void createTrigger(final StringBuilder sqlBuffer, final DataEventType dml
String triggerSql = triggerTemplate.createTriggerDDL(dml, trigger, hist, channel,
tablePrefix, table, defaultCatalog, defaultSchema);

String postTriggerDml = createPostTriggerDDL(dml, trigger, hist, channel, tablePrefix,
table);

if (parameterService.is(ParameterConstants.AUTO_SYNC_TRIGGERS)) {
ISqlTransaction transaction = null;
try {
Expand All @@ -336,20 +333,14 @@ public void createTrigger(final StringBuilder sqlBuffer, final DataEventType dml

try {
log.debug("Running: {}", triggerSql);
logSql(triggerSql, sqlBuffer);
transaction.execute(triggerSql);
} catch (SqlException ex) {
log.info("Failed to create trigger: {}", triggerSql);
throw ex;
}

if (StringUtils.isNotBlank(postTriggerDml)) {
try {
transaction.execute(postTriggerDml);
} catch (SqlException ex) {
log.info("Failed to create post trigger: {}", postTriggerDml);
throw ex;
}
}
postCreateTrigger(transaction, sqlBuffer, dml, trigger, hist, channel, tablePrefix, table);
transaction.commit();
} catch (SqlException ex) {
transaction.rollback();
Expand All @@ -366,10 +357,21 @@ public void createTrigger(final StringBuilder sqlBuffer, final DataEventType dml

}
}
}

logSql(triggerSql, sqlBuffer);
logSql(postTriggerDml, sqlBuffer);

protected void postCreateTrigger(ISqlTransaction transaction, StringBuilder sqlBuffer, DataEventType dml,
Trigger trigger, TriggerHistory hist, Channel channel, String tablePrefix, Table table) {
String postTriggerDml = createPostTriggerDDL(dml, trigger, hist, channel, tablePrefix, table);
if (StringUtils.isNotBlank(postTriggerDml)) {
try {
log.debug("Running: {}", postTriggerDml);
logSql(postTriggerDml, sqlBuffer);
transaction.execute(postTriggerDml);
} catch (SqlException ex) {
log.info("Failed to create post trigger: {}", postTriggerDml);
throw ex;
}
}
}

/*
Expand Down
10 changes: 10 additions & 0 deletions symmetric-core/src/main/resources/symmetric-default.properties
Expand Up @@ -1487,6 +1487,16 @@ mssql.use.ntypes.for.sync=false
# Tags: other, mssql
mssql.trigger.execute.as=caller

# Set the order of triggers to 'First' using sp_settriggerorder after creating triggers.
# This is needed when the user has existing custom triggers that modify data.
# The SymmetricDS triggers need to fire first and capture the first change so that order of changes is preserved.
# If the user has a trigger set as 'First', it will be changed to 'None'.
#
# DatabaseOverridable: true
# Tags: other, mssql
# Type: boolean
mssql.trigger.order.first=false

# Disables lock escalation and turns off page level locking. May need turned off to support backup processes such as creating a bacpac file
#
# DatabaseOverridable: true
Expand Down

0 comments on commit e663e24

Please sign in to comment.