diff --git a/dbdeploy-ant/src/main/java/com/dbdeploy/AntTarget.java b/dbdeploy-ant/src/main/java/com/dbdeploy/AntTarget.java index 9e35097..1fa0679 100644 --- a/dbdeploy-ant/src/main/java/com/dbdeploy/AntTarget.java +++ b/dbdeploy-ant/src/main/java/com/dbdeploy/AntTarget.java @@ -3,10 +3,12 @@ import com.dbdeploy.database.DelimiterType; import com.dbdeploy.database.LineEnding; import com.dbdeploy.exceptions.UsageException; +import com.dbdeploy.scripts.ChangeScript; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; import java.io.File; +import java.util.List; public class AntTarget extends Task { private DbDeploy dbDeploy = new DbDeploy(); @@ -102,5 +104,27 @@ public void setEncoding(String encoding) { public void setLineEnding(LineEnding lineEnding) { dbDeploy.setLineEnding(lineEnding); } + + public void setChangeScriptFilterClassName(String className) { + ChangeScriptFilter filter = newChangeSciptFilter(className); + dbDeploy.setChangeScriptFilter(filter); + } + + public void setExceptionsToContinueExecutionOn(String exceptionsCsv) { + dbDeploy.setExceptionsToContinueExecutionOn(exceptionsCsv); + } + + private ChangeScriptFilter newChangeSciptFilter(String filterClassName) { + try { + ChangeScriptFilter filter = null; + if (filterClassName != null && !"".equals(filterClassName)) { + filter = (ChangeScriptFilter) getClass().getClassLoader().loadClass(filterClassName).newInstance(); + } + return filter; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } diff --git a/dbdeploy-core/src/it/db/validation_failing_deltas/20150901299910_create_test_table.sql b/dbdeploy-core/src/it/db/validation_failing_deltas/20150901299910_create_test_table.sql new file mode 100644 index 0000000..aaeea36 --- /dev/null +++ b/dbdeploy-core/src/it/db/validation_failing_deltas/20150901299910_create_test_table.sql @@ -0,0 +1,2 @@ +CREATE TABLE Test (id INTEGER); + diff --git a/dbdeploy-core/src/it/db/validation_failing_deltas/201509102992991_insert_value_into_test_table.sql b/dbdeploy-core/src/it/db/validation_failing_deltas/201509102992991_insert_value_into_test_table.sql new file mode 100644 index 0000000..4802da7 --- /dev/null +++ b/dbdeploy-core/src/it/db/validation_failing_deltas/201509102992991_insert_value_into_test_table.sql @@ -0,0 +1 @@ +INSERT INTO Test VALUES (6); diff --git a/dbdeploy-core/src/it/db/validation_passing_deltas/20150901299910_create_test_table.sql b/dbdeploy-core/src/it/db/validation_passing_deltas/20150901299910_create_test_table.sql new file mode 100644 index 0000000..aaeea36 --- /dev/null +++ b/dbdeploy-core/src/it/db/validation_passing_deltas/20150901299910_create_test_table.sql @@ -0,0 +1,2 @@ +CREATE TABLE Test (id INTEGER); + diff --git a/dbdeploy-core/src/it/db/validation_passing_deltas/20150920220022_valid_script,sql b/dbdeploy-core/src/it/db/validation_passing_deltas/20150920220022_valid_script,sql new file mode 100644 index 0000000..46fe02d --- /dev/null +++ b/dbdeploy-core/src/it/db/validation_passing_deltas/20150920220022_valid_script,sql @@ -0,0 +1 @@ +INSERT INTO Test VALUES (6); \ No newline at end of file diff --git a/dbdeploy-core/src/main/java/com/dbdeploy/ChangeScriptFilter.java b/dbdeploy-core/src/main/java/com/dbdeploy/ChangeScriptFilter.java new file mode 100644 index 0000000..83fa496 --- /dev/null +++ b/dbdeploy-core/src/main/java/com/dbdeploy/ChangeScriptFilter.java @@ -0,0 +1,10 @@ +package com.dbdeploy; + +import com.dbdeploy.scripts.ChangeScript; + +import java.util.List; + +public interface ChangeScriptFilter { + + public void process(List changeScripts); +} diff --git a/dbdeploy-core/src/main/java/com/dbdeploy/Controller.java b/dbdeploy-core/src/main/java/com/dbdeploy/Controller.java index a76a3f1..20d401b 100644 --- a/dbdeploy-core/src/main/java/com/dbdeploy/Controller.java +++ b/dbdeploy-core/src/main/java/com/dbdeploy/Controller.java @@ -14,19 +14,32 @@ public class Controller { private final AppliedChangesProvider appliedChangesProvider; private final ChangeScriptApplier changeScriptApplier; private final ChangeScriptApplier undoScriptApplier; + private ChangeScriptFilter changeScriptFilter; - private final PrettyPrinter prettyPrinter = new PrettyPrinter(); + private final PrettyPrinter prettyPrinter = new PrettyPrinter(); public Controller(AvailableChangeScriptsProvider availableChangeScriptsProvider, - AppliedChangesProvider appliedChangesProvider, - ChangeScriptApplier changeScriptApplier, ChangeScriptApplier undoScriptApplier) { + AppliedChangesProvider appliedChangesProvider, + ChangeScriptApplier changeScriptApplier, ChangeScriptApplier undoScriptApplier, ChangeScriptFilter changeScriptFilter) { this.availableChangeScriptsProvider = availableChangeScriptsProvider; this.appliedChangesProvider = appliedChangesProvider; this.changeScriptApplier = changeScriptApplier; this.undoScriptApplier = undoScriptApplier; - } + this.changeScriptFilter = maskNull(changeScriptFilter); + } + + private ChangeScriptFilter maskNull(ChangeScriptFilter changeScriptFilter) { + if (changeScriptFilter == null) { + return new ChangeScriptFilter() { + public void process(List changeScripts) { + //no op + } + }; + } + return changeScriptFilter; + } - public void processChangeScripts(Long lastChangeToApply) throws DbDeployException, IOException { + public void processChangeScripts(Long lastChangeToApply) throws DbDeployException, IOException { if (lastChangeToApply != Long.MAX_VALUE) { info("Only applying changes up and including change script #" + lastChangeToApply); } @@ -35,6 +48,8 @@ public void processChangeScripts(Long lastChangeToApply) throws DbDeployExceptio List applied = appliedChangesProvider.getAppliedChanges(); List toApply = identifyChangesToApply(lastChangeToApply, scripts, applied); + applyFilter(toApply); + logStatus(scripts, applied, toApply); changeScriptApplier.apply(Collections.unmodifiableList(toApply)); @@ -46,6 +61,11 @@ public void processChangeScripts(Long lastChangeToApply) throws DbDeployExceptio } } + private void applyFilter(List toApply) { + changeScriptFilter.process(toApply); + } + + private void logStatus(List scripts, List applied, List toApply) { info("Changes currently applied to database:\n " + prettyPrinter.format(applied)); info("Scripts available:\n " + prettyPrinter.formatChangeScriptList(scripts)); diff --git a/dbdeploy-core/src/main/java/com/dbdeploy/DbDeploy.java b/dbdeploy-core/src/main/java/com/dbdeploy/DbDeploy.java index cad2748..e2eddc1 100644 --- a/dbdeploy-core/src/main/java/com/dbdeploy/DbDeploy.java +++ b/dbdeploy-core/src/main/java/com/dbdeploy/DbDeploy.java @@ -14,6 +14,9 @@ import java.io.File; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; public class DbDeploy { private String url; @@ -31,8 +34,10 @@ public class DbDeploy { private String delimiter = ";"; private DelimiterType delimiterType = DelimiterType.normal; private File templatedir; + private ChangeScriptFilter changeScriptFilter; + private List exceptionsToContinueExecutionOn = new ArrayList(); - public void setDriver(String driver) { + public void setDriver(String driver) { this.driver = driver; } @@ -80,51 +85,52 @@ public void setLineEnding(LineEnding lineEnding) { this.lineEnding = lineEnding; } - public void go() throws Exception { - System.err.println(getWelcomeString()); + public void go() throws Exception { + System.err.println(getWelcomeString()); - validate(); + validate(); - Class.forName(driver); + Class.forName(driver); - QueryExecuter queryExecuter = new QueryExecuter(url, userid, password); + QueryExecuter queryExecuter = new QueryExecuter(url, userid, password); - DatabaseSchemaVersionManager databaseSchemaVersionManager = - new DatabaseSchemaVersionManager(queryExecuter, changeLogTableName); + DatabaseSchemaVersionManager databaseSchemaVersionManager = + new DatabaseSchemaVersionManager(queryExecuter, changeLogTableName); - ChangeScriptRepository changeScriptRepository = - new ChangeScriptRepository(new DirectoryScanner(encoding).getChangeScriptsForDirectory(scriptdirectory)); + ChangeScriptRepository changeScriptRepository = + new ChangeScriptRepository(new DirectoryScanner(encoding).getChangeScriptsForDirectory(scriptdirectory)); - ChangeScriptApplier doScriptApplier; + ChangeScriptApplier doScriptApplier; - if (outputfile != null) { - doScriptApplier = new TemplateBasedApplier( - new PrintWriter(outputfile, encoding), dbms, - changeLogTableName, delimiter, delimiterType, getTemplatedir()); - } else { - QueryStatementSplitter splitter = new QueryStatementSplitter(); - splitter.setDelimiter(getDelimiter()); - splitter.setDelimiterType(getDelimiterType()); - splitter.setOutputLineEnding(lineEnding); - doScriptApplier = new DirectToDbApplier(queryExecuter, databaseSchemaVersionManager, splitter); - } + if (outputfile != null) { + doScriptApplier = new TemplateBasedApplier( + new PrintWriter(outputfile, encoding), dbms, + changeLogTableName, delimiter, delimiterType, getTemplatedir()); + } else { + QueryStatementSplitter splitter = new QueryStatementSplitter(); + splitter.setDelimiter(getDelimiter()); + splitter.setDelimiterType(getDelimiterType()); + splitter.setOutputLineEnding(lineEnding); + doScriptApplier = new DirectToDbApplier(queryExecuter, databaseSchemaVersionManager, splitter, exceptionsToContinueExecutionOn); - ChangeScriptApplier undoScriptApplier = null; + } - if (undoOutputfile != null) { - undoScriptApplier = new UndoTemplateBasedApplier( - new PrintWriter(undoOutputfile), dbms, changeLogTableName, delimiter, delimiterType, templatedir); + ChangeScriptApplier undoScriptApplier = null; - } + if (undoOutputfile != null) { + undoScriptApplier = new UndoTemplateBasedApplier( + new PrintWriter(undoOutputfile), dbms, changeLogTableName, delimiter, delimiterType, templatedir); - Controller controller = new Controller(changeScriptRepository, databaseSchemaVersionManager, doScriptApplier, undoScriptApplier); + } - controller.processChangeScripts(lastChangeToApply); + Controller controller = new Controller(changeScriptRepository, databaseSchemaVersionManager, doScriptApplier, undoScriptApplier, changeScriptFilter); - queryExecuter.close(); - } + controller.processChangeScripts(lastChangeToApply); + + queryExecuter.close(); + } - private void validate() throws UsageException { + private void validate() throws UsageException { checkForRequiredParameter(userid, "userid"); checkForRequiredParameter(driver, "driver"); checkForRequiredParameter(url, "url"); @@ -224,4 +230,19 @@ public String getEncoding() { public LineEnding getLineEnding() { return lineEnding; } + + public void setChangeScriptFilter(ChangeScriptFilter changeScriptFilter) { + this.changeScriptFilter = changeScriptFilter; + } + + public void setExceptionsToContinueExecutionOn(String exceptionsCsv) { + StringTokenizer tokenizer = new StringTokenizer(exceptionsCsv, ","); + while(tokenizer.hasMoreTokens()) { + exceptionsToContinueExecutionOn.add(tokenizer.nextToken()); + } + } + + public List getExceptionsToContinueExecutionOn() { + return exceptionsToContinueExecutionOn; + } } diff --git a/dbdeploy-core/src/main/java/com/dbdeploy/appliers/DirectToDbApplier.java b/dbdeploy-core/src/main/java/com/dbdeploy/appliers/DirectToDbApplier.java index 663caca..35fa6dc 100644 --- a/dbdeploy-core/src/main/java/com/dbdeploy/appliers/DirectToDbApplier.java +++ b/dbdeploy-core/src/main/java/com/dbdeploy/appliers/DirectToDbApplier.java @@ -4,37 +4,68 @@ import com.dbdeploy.database.QueryStatementSplitter; import com.dbdeploy.database.changelog.DatabaseSchemaVersionManager; import com.dbdeploy.database.changelog.QueryExecuter; +import com.dbdeploy.exceptions.ChangeScriptApplierException; import com.dbdeploy.exceptions.ChangeScriptFailedException; import com.dbdeploy.scripts.ChangeScript; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; public class DirectToDbApplier implements ChangeScriptApplier { private final QueryExecuter queryExecuter; private final DatabaseSchemaVersionManager schemaVersionManager; private final QueryStatementSplitter splitter; + private List exceptionsToContinueExecutionOn; - public DirectToDbApplier(QueryExecuter queryExecuter, DatabaseSchemaVersionManager schemaVersionManager, QueryStatementSplitter splitter) { + public DirectToDbApplier(QueryExecuter queryExecuter, DatabaseSchemaVersionManager schemaVersionManager, QueryStatementSplitter splitter, List exceptionsToContinueExecutionOn) { this.queryExecuter = queryExecuter; this.schemaVersionManager = schemaVersionManager; this.splitter = splitter; + this.exceptionsToContinueExecutionOn = exceptionsToContinueExecutionOn; } public void apply(List changeScript) { begin(); + List scriptFailedExceptions = new ArrayList(); for (ChangeScript script : changeScript) { System.err.println("Applying " + script + "..."); - applyChangeScript(script); - insertToSchemaVersionTable(script); + try { - commitTransaction(); + apply(script); + + } catch (ChangeScriptFailedException e) { + String errorMessage = e.getCause().getMessage(); + scriptFailedExceptions.add(e); + if (!containsMessagePartToIgnore(exceptionsToContinueExecutionOn, errorMessage)) { + break; + } + } + } + + if (!scriptFailedExceptions.isEmpty()) { + throw new ChangeScriptApplierException(scriptFailedExceptions); + } + } + + private boolean containsMessagePartToIgnore(List exceptionsToIgnore, String errorMessage) { + for (String ignoreToken : exceptionsToIgnore) { + if (errorMessage.contains(ignoreToken)) { + return true; + } } + return false; + } + + private void apply(ChangeScript script) { + applyChangeScript(script); + insertToSchemaVersionTable(script); + commitTransaction(); } - public void begin() { + public void begin() { try { queryExecuter.setAutoCommit(false); } catch (SQLException e) { @@ -69,6 +100,4 @@ protected void commitTransaction() { throw new RuntimeException(); } } - - } diff --git a/dbdeploy-core/src/main/java/com/dbdeploy/exceptions/ChangeScriptApplierException.java b/dbdeploy-core/src/main/java/com/dbdeploy/exceptions/ChangeScriptApplierException.java new file mode 100644 index 0000000..4b140d9 --- /dev/null +++ b/dbdeploy-core/src/main/java/com/dbdeploy/exceptions/ChangeScriptApplierException.java @@ -0,0 +1,58 @@ +package com.dbdeploy.exceptions; + +import java.util.List; + +public class ChangeScriptApplierException extends DbDeployException { + List changeScriptExceptions; + + public ChangeScriptApplierException(List changeScriptExceptions) { + this.changeScriptExceptions = changeScriptExceptions; + } + + public List getChangeScriptExceptions() { + return changeScriptExceptions; + } + + @Override + public String getMessage() { + StringBuilder message = new StringBuilder(); + message.append(buildMessage()); + message.append("\n"); + message.append("\n"); + message.append(buildSuggession()); + return message.toString(); + + } + + private String buildSuggession() { + StringBuilder suggession = new StringBuilder(); + suggession.append("If these scripts are already executed, run following command"); + suggession.append("\n"); + suggession.append("gradle -PjdbcUrl= -PjdbcUserName= -PjdbcPassword= -Pmigrations="); + suggession.append(changeScriptNamesCsv()); + suggession.append(" :consultingdb:putMigrationInVersionTable"); + return suggession.toString(); + + } + + private String changeScriptNamesCsv() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < changeScriptExceptions.size(); i++) { + String scriptName = changeScriptExceptions.get(i).getScript().getFile().getName(); + sb.append(scriptName); + if (i != (changeScriptExceptions.size() - 1)) { + sb.append(","); + } + } + return sb.toString(); + } + + private String buildMessage() { + StringBuilder message = new StringBuilder(); + for (ChangeScriptFailedException changeScriptException : changeScriptExceptions) { + message.append(changeScriptException.getMessage()); + message.append("\n"); + } + return message.toString(); + } +} diff --git a/dbdeploy-core/src/main/java/com/dbdeploy/scripts/ChangeScriptRepository.java b/dbdeploy-core/src/main/java/com/dbdeploy/scripts/ChangeScriptRepository.java index 8498081..4eae18d 100644 --- a/dbdeploy-core/src/main/java/com/dbdeploy/scripts/ChangeScriptRepository.java +++ b/dbdeploy-core/src/main/java/com/dbdeploy/scripts/ChangeScriptRepository.java @@ -1,8 +1,9 @@ package com.dbdeploy.scripts; -import com.dbdeploy.exceptions.DuplicateChangeScriptException; import com.dbdeploy.AvailableChangeScriptsProvider; +import com.dbdeploy.exceptions.DuplicateChangeScriptException; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -22,17 +23,32 @@ public ChangeScriptRepository(List scripts) throws DuplicateChange private void checkForDuplicateIds(List scripts) throws DuplicateChangeScriptException { long lastId = -1; - + + List duplicateIds = new ArrayList(); for (ChangeScript script : scripts) { if (script.getId() == lastId) { - throw new DuplicateChangeScriptException("There is more than one change script with number " + lastId); + duplicateIds.add(script.getId()); } - + lastId = script.getId(); } - + if (!duplicateIds.isEmpty()) { + throw new DuplicateChangeScriptException("There is more than one change script with number " + toCSV(duplicateIds)); + } + } + private String toCSV(List c) { + String str = ""; + for (int i = 0; i < c.size(); i++) { + if (i != 0) { + str = str + ","; + } + str = str + c.get(i).toString(); + } + return str; + } + public List getOrderedListOfDoChangeScripts() { return Collections.unmodifiableList(scripts); } diff --git a/dbdeploy-core/src/test/java/com/dbdeploy/ChangeScriptValidationFailedException.java b/dbdeploy-core/src/test/java/com/dbdeploy/ChangeScriptValidationFailedException.java new file mode 100644 index 0000000..d737ca8 --- /dev/null +++ b/dbdeploy-core/src/test/java/com/dbdeploy/ChangeScriptValidationFailedException.java @@ -0,0 +1,20 @@ +package com.dbdeploy; + +import com.dbdeploy.exceptions.DbDeployException; + +public class ChangeScriptValidationFailedException extends DbDeployException { + public ChangeScriptValidationFailedException() { + } + + public ChangeScriptValidationFailedException(String message) { + super(message); + } + + public ChangeScriptValidationFailedException(String message, Throwable cause) { + super(message, cause); + } + + public ChangeScriptValidationFailedException(Throwable cause) { + super(cause); + } +} diff --git a/dbdeploy-core/src/test/java/com/dbdeploy/ChangeScriptValidator.java b/dbdeploy-core/src/test/java/com/dbdeploy/ChangeScriptValidator.java new file mode 100644 index 0000000..888010d --- /dev/null +++ b/dbdeploy-core/src/test/java/com/dbdeploy/ChangeScriptValidator.java @@ -0,0 +1,8 @@ +package com.dbdeploy; + +import com.dbdeploy.exceptions.DbDeployException; +import com.dbdeploy.scripts.ChangeScript; + +public interface ChangeScriptValidator { + boolean validate(ChangeScript changeScript) throws DbDeployException; +} diff --git a/dbdeploy-core/src/test/java/com/dbdeploy/ChangeScriptValidatorProvider.java b/dbdeploy-core/src/test/java/com/dbdeploy/ChangeScriptValidatorProvider.java new file mode 100644 index 0000000..1070e72 --- /dev/null +++ b/dbdeploy-core/src/test/java/com/dbdeploy/ChangeScriptValidatorProvider.java @@ -0,0 +1,11 @@ +package com.dbdeploy; + +import com.dbdeploy.scripts.ChangeScript; + +import java.util.List; + +public class ChangeScriptValidatorProvider implements ChangeScriptFilter { + public void process(List changeScripts) { + + } +} diff --git a/dbdeploy-core/src/test/java/com/dbdeploy/ControllerTest.java b/dbdeploy-core/src/test/java/com/dbdeploy/ControllerTest.java index b08438b..71daa2c 100644 --- a/dbdeploy-core/src/test/java/com/dbdeploy/ControllerTest.java +++ b/dbdeploy-core/src/test/java/com/dbdeploy/ControllerTest.java @@ -1,13 +1,10 @@ package com.dbdeploy; import com.dbdeploy.scripts.ChangeScript; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import static org.mockito.Mockito.when; import org.mockito.runners.MockitoJUnit44Runner; import java.util.ArrayList; @@ -15,6 +12,10 @@ import java.util.Collections; import java.util.List; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.when; + @RunWith(MockitoJUnit44Runner.class) public class ControllerTest { @@ -27,10 +28,14 @@ public class ControllerTest { private StubChangeScriptApplier applier = new StubChangeScriptApplier(); private StubChangeScriptApplier undoApplier = new StubChangeScriptApplier(); - + private ChangeScriptFilter changeScriptFilter = new ChangeScriptFilter() { + public void process(List changeScripts) { + //noop + } + }; @Before public void setUp() { - controller = new Controller(availableChangeScriptsProvider, appliedChangesProvider, applier, undoApplier); + controller = new Controller(availableChangeScriptsProvider, appliedChangesProvider, applier, undoApplier, changeScriptFilter); change1 = new ChangeScript(1); change2 = new ChangeScript(2); @@ -54,7 +59,7 @@ public void shouldApplyChangeScriptsInOrder() throws Exception { @Test public void shouldNotCrashWhenPassedANullUndoApplier() throws Exception { - controller = new Controller(availableChangeScriptsProvider, appliedChangesProvider, applier, null); + controller = new Controller(availableChangeScriptsProvider, appliedChangesProvider, applier, null, changeScriptFilter); when(appliedChangesProvider.getAppliedChanges()).thenReturn(Collections.emptyList()); diff --git a/dbdeploy-core/src/test/java/com/dbdeploy/DbDeployTest.java b/dbdeploy-core/src/test/java/com/dbdeploy/DbDeployTest.java index c90373a..1db347f 100644 --- a/dbdeploy-core/src/test/java/com/dbdeploy/DbDeployTest.java +++ b/dbdeploy-core/src/test/java/com/dbdeploy/DbDeployTest.java @@ -1,12 +1,16 @@ package com.dbdeploy; import com.dbdeploy.exceptions.UsageException; -import static org.hamcrest.Matchers.startsWith; -import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; import java.io.File; +import java.util.List; + +import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; public class DbDeployTest { private final DbDeploy dbDeploy = new DbDeploy(); @@ -65,4 +69,13 @@ public void shouldReportVersionNumberWithoutCrashing() { } + @Test + public void shouldParseExceptionsListCsv() { + dbDeploy.setExceptionsToContinueExecutionOn("ORA-001,ORA-002: duplicate column,ORA-003"); + List exceptionsToIgnore = dbDeploy.getExceptionsToContinueExecutionOn(); + assertEquals(3, exceptionsToIgnore.size()); + assertEquals("ORA-001", exceptionsToIgnore.get(0)); + assertEquals("ORA-002: duplicate column", exceptionsToIgnore.get(1)); + assertEquals("ORA-003", exceptionsToIgnore.get(2)); + } } diff --git a/dbdeploy-core/src/test/java/com/dbdeploy/appliers/DirectToDbApplierTest.java b/dbdeploy-core/src/test/java/com/dbdeploy/appliers/DirectToDbApplierTest.java index 32b4bf8..d95629f 100644 --- a/dbdeploy-core/src/test/java/com/dbdeploy/appliers/DirectToDbApplierTest.java +++ b/dbdeploy-core/src/test/java/com/dbdeploy/appliers/DirectToDbApplierTest.java @@ -3,6 +3,7 @@ import com.dbdeploy.database.QueryStatementSplitter; import com.dbdeploy.database.changelog.DatabaseSchemaVersionManager; import com.dbdeploy.database.changelog.QueryExecuter; +import com.dbdeploy.exceptions.ChangeScriptApplierException; import com.dbdeploy.exceptions.ChangeScriptFailedException; import com.dbdeploy.scripts.ChangeScript; import com.dbdeploy.scripts.StubChangeScript; @@ -13,12 +14,18 @@ import org.mockito.runners.MockitoJUnit44Runner; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Arrays; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.internal.verification.VerificationModeFactory.times; @RunWith(MockitoJUnit44Runner.class) public class DirectToDbApplierTest { @@ -29,7 +36,8 @@ public class DirectToDbApplierTest { @Before public void setUp() { - applier = new DirectToDbApplier(queryExecuter, schemaVersionManager, splitter); + + applier = new DirectToDbApplier(queryExecuter, schemaVersionManager, splitter, new ArrayList()); } @Test @@ -84,5 +92,91 @@ public void shouldCommitTransactionOnErrrCommitTransaction() throws Exception { verify(queryExecuter).commit(); } + @Test + public void shouldContinueOnErrorIfErrorToIgnoreAreProvided() throws Exception + { + applier = new DirectToDbApplier(queryExecuter, schemaVersionManager, splitter, Arrays.asList("ORA-0001: unique constraint")); + + when(splitter.split("split1; content1")).thenReturn(Arrays.asList("split1", "content1")); + when(splitter.split("content2")).thenReturn(Arrays.asList("content2")); + + ChangeScript script1 = new StubChangeScript(1, "script", "split1; content1"); + ChangeScript script2 = new StubChangeScript(2, "script", "content2"); + + doThrow(new SQLException("ORA-0001: unique constraint")).when(queryExecuter).execute("split1"); + + ChangeScriptApplierException expectedException = null; + try { + applier.apply(Arrays.asList(script1, script2)); + + } catch (ChangeScriptApplierException e) { + expectedException = e; + } + + verify(queryExecuter).execute("split1"); + verify(queryExecuter).execute("content2"); + + ChangeScriptFailedException scriptException = expectedException.getChangeScriptExceptions().get(0); + assertThat(scriptException .getExecutedSql(), is("split1")); + assertThat(scriptException .getScript(), is(script1)); + } + + @Test + public void shouldStopExecutionIfExceptionIsOtherThanIgnored() throws SQLException { + applier = new DirectToDbApplier(queryExecuter, schemaVersionManager, splitter, Arrays.asList("ORA-0001: unique constraint")); + + when(splitter.split("split1; content1")).thenReturn(Arrays.asList("split1", "content1")); + when(splitter.split("content2")).thenReturn(Arrays.asList("content2")); + + ChangeScript script1 = new StubChangeScript(1, "script", "split1; content1"); + ChangeScript script2 = new StubChangeScript(2, "script", "content2"); + + doThrow(new SQLException("ORA-0002: table or view does not exist")).when(queryExecuter).execute("split1"); + + ChangeScriptApplierException expectedException = null; + try { + applier.apply(Arrays.asList(script1, script2)); + + } catch (ChangeScriptApplierException e) { + expectedException = e; + } + + assertNotNull(expectedException); + verify(queryExecuter).execute("split1"); + verify(queryExecuter, times(0)).execute("content2"); + + } + + + + @Test + public void shouldStopExecutionAndGiveAllPreviousExceptionsIfToIgnoreAreProvided() throws SQLException { + applier = new DirectToDbApplier(queryExecuter, schemaVersionManager, splitter, Arrays.asList("ORA-0001: unique constraint")); + + when(splitter.split("split1; content1")).thenReturn(Arrays.asList("split1", "content1")); + when(splitter.split("content2")).thenReturn(Arrays.asList("content2")); + + ChangeScript script1 = new StubChangeScript(1, "script", "split1; content1"); + ChangeScript script2 = new StubChangeScript(2, "script", "content2"); + + doThrow(new SQLException("ORA-0001: unique constraint")).when(queryExecuter).execute("split1"); + doThrow(new SQLException("ORA-0002: table or view does not exist")).when(queryExecuter).execute("content2"); + + ChangeScriptApplierException expectedException = null; + try { + applier.apply(Arrays.asList(script1, script2)); + + } catch (ChangeScriptApplierException e) { + expectedException = e; + } + + assertNotNull(expectedException); + ChangeScriptFailedException scriptException = expectedException.getChangeScriptExceptions().get(0); + assertThat(scriptException .getExecutedSql(), is("split1")); + assertThat(scriptException.getScript(), is(script1)); + scriptException = expectedException.getChangeScriptExceptions().get(1); + assertThat(scriptException .getExecutedSql(), is("content2")); + assertThat(scriptException.getScript(), is(script2)); + } } diff --git a/dbdeploy-core/src/test/java/com/dbdeploy/database/ScriptGenerationTest.java b/dbdeploy-core/src/test/java/com/dbdeploy/database/ScriptGenerationTest.java index 58215da..091a25b 100644 --- a/dbdeploy-core/src/test/java/com/dbdeploy/database/ScriptGenerationTest.java +++ b/dbdeploy-core/src/test/java/com/dbdeploy/database/ScriptGenerationTest.java @@ -2,6 +2,7 @@ import com.dbdeploy.ChangeScriptApplier; import com.dbdeploy.Controller; +import com.dbdeploy.ChangeScriptFilter; import com.dbdeploy.appliers.TemplateBasedApplier; import com.dbdeploy.database.changelog.DatabaseSchemaVersionManager; import com.dbdeploy.exceptions.SchemaVersionTrackingException; @@ -10,7 +11,12 @@ import com.dbdeploy.scripts.StubChangeScript; import org.junit.Test; -import java.io.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -45,7 +51,12 @@ private void runIntegratedTestAndConfirmOutputResults(String syntaxName) throws final StubSchemaManager schemaManager = new StubSchemaManager(); ChangeScriptApplier applier = new TemplateBasedApplier(writer, syntaxName, "changelog", ";", DelimiterType.normal, null); - Controller controller = new Controller(changeScriptRepository, schemaManager, applier, null); + ChangeScriptFilter chain = new ChangeScriptFilter() { + public void process(List changeScripts) { + + } + }; + Controller controller = new Controller(changeScriptRepository, schemaManager, applier, null, chain); controller.processChangeScripts(Long.MAX_VALUE); diff --git a/dbdeploy-core/src/test/java/com/dbdeploy/integration/ChangeScriptValidatingFilter.java b/dbdeploy-core/src/test/java/com/dbdeploy/integration/ChangeScriptValidatingFilter.java new file mode 100644 index 0000000..fb4180d --- /dev/null +++ b/dbdeploy-core/src/test/java/com/dbdeploy/integration/ChangeScriptValidatingFilter.java @@ -0,0 +1,42 @@ +package com.dbdeploy.integration; + + +import com.dbdeploy.ChangeScriptValidator; +import com.dbdeploy.ChangeScriptFilter; +import com.dbdeploy.ChangeScriptValidationFailedException; +import com.dbdeploy.exceptions.DbDeployException; +import com.dbdeploy.scripts.ChangeScript; + +import java.util.ArrayList; +import java.util.List; + +public class ChangeScriptValidatingFilter implements ChangeScriptFilter { + + private List getValidators() { + List validators = new ArrayList(); + validators.add(new ChangeScriptValidator() { + public boolean validate(ChangeScript changeScript) throws DbDeployException { + String changeScriptName = changeScript.getFile().getName(); + if (!changeScriptName.matches("[0-9]{14}_(.*)")) { + throw new ChangeScriptValidationFailedException("name should start with datetime format yyyyMMddHHmmss"); + } + return Boolean.TRUE; + } + }); + return validators; + } + + public void process(List changeScripts) { + for (ChangeScript changeScript : changeScripts) { + applyValidations(changeScript); + } + } + + private void applyValidations(ChangeScript changeScript) { + List validators = getValidators(); + for (ChangeScriptValidator validator : validators) { + validator.validate(changeScript); + } + + } +} \ No newline at end of file diff --git a/dbdeploy-core/src/test/java/com/dbdeploy/integration/Database.java b/dbdeploy-core/src/test/java/com/dbdeploy/integration/Database.java index f822af5..bd49a7d 100644 --- a/dbdeploy-core/src/test/java/com/dbdeploy/integration/Database.java +++ b/dbdeploy-core/src/test/java/com/dbdeploy/integration/Database.java @@ -40,7 +40,7 @@ private Connection openConnection() throws ClassNotFoundException, SQLException public void createSchemaVersionTable() throws SQLException { execute("CREATE TABLE " + changeLogTableName + " ( " + - " change_number INTEGER NOT NULL, " + + " change_number BIGINT NOT NULL, " + " complete_dt TIMESTAMP NOT NULL, " + " applied_by VARCHAR(100) NOT NULL, " + " description VARCHAR(500) NOT NULL " + diff --git a/dbdeploy-core/src/test/java/com/dbdeploy/integration/DirectToDbIntegrationTest.java b/dbdeploy-core/src/test/java/com/dbdeploy/integration/DirectToDbIntegrationTest.java index 9290fbd..50b3ec6 100644 --- a/dbdeploy-core/src/test/java/com/dbdeploy/integration/DirectToDbIntegrationTest.java +++ b/dbdeploy-core/src/test/java/com/dbdeploy/integration/DirectToDbIntegrationTest.java @@ -1,13 +1,17 @@ package com.dbdeploy.integration; import com.dbdeploy.DbDeploy; +import com.dbdeploy.ChangeScriptValidationFailedException; import org.junit.Test; import java.io.File; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.hasItems; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; import static org.junit.Assert.fail; public class DirectToDbIntegrationTest { @@ -45,6 +49,32 @@ public void shouldSuccessfullyApplyAValidSetOfDeltasIncludingMutliStatementDelta assertThat(results, hasItems(new Object[] {6}, new Object[] {7})); } + @Test + public void shouldApplyValidateChangeScripts() throws Exception { + Database db = new Database("todb_custaom_validator_passing_test"); + db.createSchemaVersionTable(); + + DbDeploy dbDeploy = new DbDeploy(); + db.applyDatabaseSettingsTo(dbDeploy); + dbDeploy.setChangeScriptFilter(new ChangeScriptValidatingFilter()); + dbDeploy.setScriptdirectory(findScriptDirectory("src/it/db/validation_passing_deltas")); + dbDeploy.go(); + + assertThat(db.getChangelogEntries(), hasItems(20150920220022L)); + } + + + @Test(expected = ChangeScriptValidationFailedException.class) + public void shouldValidateChangeScriptsToBeAppliedProvidedByValidatorFactory() throws Exception { + Database db = new Database("todb__custom_validator_failing_test"); + db.createSchemaVersionTable(); + + DbDeploy dbDeploy = new DbDeploy(); + db.applyDatabaseSettingsTo(dbDeploy); + dbDeploy.setChangeScriptFilter(new ChangeScriptValidatingFilter()); + dbDeploy.setScriptdirectory(findScriptDirectory("src/it/db/validation_failing_deltas")); + dbDeploy.go(); + } @Test public void shouldBeAbleToRecoverFromBadScriptsJustByRunningCorrectedScriptsAgain() throws Exception {