├── .gitignore ├── dbdeploy-ant ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── dbdeploy │ ├── AntTarget.java │ └── CreateChangeScriptTarget.java ├── dbdeploy-cli ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── dbdeploy │ │ ├── CommandLineTarget.java │ │ ├── DbDeployCommandLineParser.java │ │ └── UserInputReader.java │ └── test │ └── java │ └── com │ └── dbdeploy │ └── DbDeployCommandLineParserTest.java ├── dbdeploy-core ├── pom.xml └── src │ ├── it │ └── db │ │ ├── deltas │ │ ├── 001_create_test_table.sql │ │ └── 002_insert_value_into_test_table.sql │ │ ├── high_numbers │ │ └── 1000_high_number.sql │ │ ├── invalid_deltas │ │ ├── 001_create_test_table.sql │ │ └── 002_insert_value_into_test_table.sql │ │ └── multi_statement_deltas │ │ ├── 001_create_test_table.sql │ │ └── 002_insert_values_into_test_table.sql │ ├── main │ ├── java │ │ └── com │ │ │ └── dbdeploy │ │ │ ├── AppliedChangesProvider.java │ │ │ ├── AvailableChangeScriptsProvider.java │ │ │ ├── ChangeScriptApplier.java │ │ │ ├── Controller.java │ │ │ ├── DbDeploy.java │ │ │ ├── PrettyPrinter.java │ │ │ ├── appliers │ │ │ ├── ApplyMode.java │ │ │ ├── DirectToDbApplier.java │ │ │ ├── TemplateBasedApplier.java │ │ │ └── UndoTemplateBasedApplier.java │ │ │ ├── database │ │ │ ├── DelimiterType.java │ │ │ ├── LineEnding.java │ │ │ ├── QueryStatementSplitter.java │ │ │ └── changelog │ │ │ │ ├── DatabaseSchemaVersionManager.java │ │ │ │ └── QueryExecuter.java │ │ │ ├── exceptions │ │ │ ├── ChangeScriptFailedException.java │ │ │ ├── DbDeployException.java │ │ │ ├── DuplicateChangeScriptException.java │ │ │ ├── SchemaVersionTrackingException.java │ │ │ ├── UnrecognisedFilenameException.java │ │ │ └── UsageException.java │ │ │ └── scripts │ │ │ ├── ChangeScript.java │ │ │ ├── ChangeScriptCreator.java │ │ │ ├── ChangeScriptRepository.java │ │ │ ├── DirectoryScanner.java │ │ │ └── FilenameParser.java │ └── resources │ │ ├── db2_apply.ftl │ │ ├── db2_undo.ftl │ │ ├── hsql_apply.ftl │ │ ├── hsql_undo.ftl │ │ ├── mssql_apply.ftl │ │ ├── mssql_undo.ftl │ │ ├── mysql_apply.ftl │ │ ├── mysql_undo.ftl │ │ ├── ora_apply.ftl │ │ ├── ora_undo.ftl │ │ ├── pgsql_apply.ftl │ │ ├── pgsql_undo.ftl │ │ ├── syb-ase_apply.ftl │ │ └── syb-ase_undo.ftl │ └── test │ ├── java │ └── com │ │ └── dbdeploy │ │ ├── ControllerTest.java │ │ ├── DbDeployTest.java │ │ ├── PrettyPrinterTest.java │ │ ├── appliers │ │ ├── DirectToDbApplierTest.java │ │ └── TemplateBasedApplierTest.java │ │ ├── database │ │ ├── QueryStatementSplitterTest.java │ │ ├── ScriptGenerationTest.java │ │ └── changelog │ │ │ └── DatabaseSchemaVersionManagerTest.java │ │ ├── integration │ │ ├── Database.java │ │ ├── DirectToDbIntegrationTest.java │ │ └── OutputToFileIntegrationTest.java │ │ └── scripts │ │ ├── ChangeScriptCreatorTest.java │ │ ├── ChangeScriptRepositoryTest.java │ │ ├── ChangeScriptTest.java │ │ ├── FilenameParserTest.java │ │ └── StubChangeScript.java │ └── resources │ └── com │ └── dbdeploy │ └── database │ ├── db2_expected.sql │ ├── hsql_expected.sql │ ├── mssql_expected.sql │ ├── mysql_expected.sql │ ├── ora_expected.sql │ ├── pgsql_expected.sql │ └── syb-ase_expected.sql ├── dbdeploy-dist ├── pom.xml └── src │ └── main │ ├── doc │ ├── LICENSE │ └── README │ ├── example │ ├── 001_create_table.sql │ ├── 002_insert_data.sql │ ├── build.xml │ ├── cli_example.sh │ └── pom.xml │ ├── resources │ └── assemblies │ │ └── distribution.xml │ └── scripts │ ├── createSchemaVersionTable.db2.sql │ ├── createSchemaVersionTable.hsql.sql │ ├── createSchemaVersionTable.mssql.sql │ ├── createSchemaVersionTable.mysql.sql │ ├── createSchemaVersionTable.ora.sql │ └── createSchemaVersionTable.syb-ase.sql ├── maven-dbdeploy-plugin ├── pom.xml └── src │ ├── main │ └── java │ │ └── com │ │ └── dbdeploy │ │ └── mojo │ │ ├── AbstractDbDeployMojo.java │ │ ├── CreateChangeScriptMojo.java │ │ ├── CreateDatabaseScriptsMojo.java │ │ └── UpdateDatabaseMojo.java │ └── test │ ├── java │ └── com │ │ └── dbdeploy │ │ └── mojo │ │ ├── CreateChangeScriptMojoTest.java │ │ ├── CreateDatabaseScriptsMojoTest.java │ │ └── UpdateDatabaseMojoTest.java │ └── resources │ └── unit │ └── test │ ├── create-change-script-plugin-config.xml │ ├── db-scripts-plugin-config.xml │ └── update-plugin-config.xml ├── pom.xml ├── todo.txt └── validate_distribution_examples.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.ipr 2 | *.iws 3 | *.iml 4 | target 5 | .idea 6 | *~ 7 | -------------------------------------------------------------------------------- /dbdeploy-ant/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | com.dbdeploy 6 | dbdeploy-parent 7 | 3.0-SNAPSHOT 8 | 9 | 10 | dbdeploy-ant 11 | jar 12 | dbdeploy-ant 13 | ant plugin to invoke dbdeploy 14 | 15 | 16 | 17 | 18 | org.apache.maven.plugins 19 | maven-source-plugin 20 | 21 | 22 | package 23 | 24 | jar 25 | 26 | 27 | 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-shade-plugin 33 | 34 | 35 | package 36 | 37 | shade 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | ${project.groupId} 48 | dbdeploy-core 49 | ${project.version} 50 | 51 | 52 | org.apache.ant 53 | ant 54 | 1.7.0 55 | provided 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /dbdeploy-ant/src/main/java/com/dbdeploy/AntTarget.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy; 2 | 3 | import com.dbdeploy.database.DelimiterType; 4 | import com.dbdeploy.database.LineEnding; 5 | import com.dbdeploy.exceptions.UsageException; 6 | import org.apache.tools.ant.BuildException; 7 | import org.apache.tools.ant.Task; 8 | 9 | import java.io.File; 10 | 11 | public class AntTarget extends Task { 12 | private DbDeploy dbDeploy = new DbDeploy(); 13 | 14 | private static String ANT_USAGE = "\n\nDbdeploy Ant Task Usage" 15 | + "\n=======================" 16 | + "\n\n\t" 32 | + "\n\n* - Indicates mandatory parameter"; 33 | 34 | @Override 35 | public void execute() throws BuildException { 36 | try { 37 | dbDeploy.go(); 38 | } catch (UsageException ex) { 39 | System.err.println(ANT_USAGE); 40 | throw new BuildException(ex.getMessage()); 41 | } catch (Exception ex) { 42 | throw new BuildException(ex); 43 | } 44 | } 45 | 46 | public void setDir(File dir) { 47 | dbDeploy.setScriptdirectory(dir); 48 | } 49 | 50 | public void setDriver(String driver) { 51 | dbDeploy.setDriver(driver); 52 | } 53 | 54 | public void setUrl(String url) { 55 | dbDeploy.setUrl(url); 56 | } 57 | 58 | public void setPassword(String password) { 59 | dbDeploy.setPassword(password); 60 | } 61 | 62 | public void setUserid(String userid) { 63 | dbDeploy.setUserid(userid); 64 | } 65 | 66 | public void setOutputfile(File outputfile) { 67 | dbDeploy.setOutputfile(outputfile); 68 | } 69 | 70 | public void setDbms(String dbms) { 71 | dbDeploy.setDbms(dbms); 72 | } 73 | 74 | public void setLastChangeToApply(Long lastChangeToApply) { 75 | dbDeploy.setLastChangeToApply(lastChangeToApply); 76 | } 77 | 78 | public void setUndoOutputfile(File undoOutputfile) { 79 | dbDeploy.setUndoOutputfile(undoOutputfile); 80 | } 81 | 82 | public void setChangeLogTableName(String changeLogTableName) { 83 | dbDeploy.setChangeLogTableName(changeLogTableName); 84 | } 85 | 86 | public void setDelimiter(String delimiter) { 87 | dbDeploy.setDelimiter(delimiter); 88 | } 89 | 90 | public void setDelimitertype(DelimiterType delimiterType) { 91 | dbDeploy.setDelimiterType(delimiterType); 92 | } 93 | 94 | public void setTemplatedir(File templateDirectory) { 95 | dbDeploy.setTemplatedir(templateDirectory); 96 | } 97 | 98 | public void setEncoding(String encoding) { 99 | dbDeploy.setEncoding(encoding); 100 | } 101 | 102 | public void setLineEnding(LineEnding lineEnding) { 103 | dbDeploy.setLineEnding(lineEnding); 104 | } 105 | } 106 | 107 | -------------------------------------------------------------------------------- /dbdeploy-ant/src/main/java/com/dbdeploy/CreateChangeScriptTarget.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy; 2 | 3 | import com.dbdeploy.exceptions.UsageException; 4 | import com.dbdeploy.scripts.ChangeScriptCreator; 5 | import java.io.File; 6 | import org.apache.tools.ant.BuildException; 7 | import org.apache.tools.ant.Task; 8 | 9 | /** 10 | * Ant task that will create a new dbdeploy 11 | * change script with a timestamp and 12 | * specified file name. 13 | * 14 | * @author jbogan 15 | */ 16 | public class CreateChangeScriptTarget extends Task { 17 | final ChangeScriptCreator changeScriptCreator = new ChangeScriptCreator(); 18 | 19 | private static String ANT_USAGE = "\n\nDbdeploy Create Script Ant Task Usage" 20 | + "\n=======================" 21 | + "\n\n\t" 25 | + "\n\n* - Indicates mandatory parameter"; 26 | 27 | @Override 28 | public void execute() throws BuildException { 29 | try { 30 | changeScriptCreator.go(); 31 | } catch (UsageException ex) { 32 | System.err.println(ANT_USAGE); 33 | throw new BuildException(ex.getMessage()); 34 | } catch (Exception ex) { 35 | throw new BuildException(ex); 36 | } 37 | } 38 | 39 | public void setName(final String name) { 40 | changeScriptCreator.setScriptDescription(name); 41 | } 42 | 43 | public void setDir(final File dir) { 44 | changeScriptCreator.setScriptDirectory(dir); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /dbdeploy-cli/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | com.dbdeploy 6 | dbdeploy-parent 7 | 3.0-SNAPSHOT 8 | 9 | 10 | dbdeploy-cli 11 | jar 12 | dbdeploy-cli 13 | Command line interface to dbdeploy 14 | 15 | 16 | 17 | 18 | 19 | 20 | maven-assembly-plugin 21 | 22 | 23 | 24 | com.dbdeploy.CommandLineTarget 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | org.apache.maven.plugins 34 | maven-source-plugin 35 | 36 | 37 | package 38 | 39 | jar 40 | 41 | 42 | 43 | 44 | 45 | 46 | org.apache.maven.plugins 47 | maven-shade-plugin 48 | 49 | 50 | package 51 | 52 | shade 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | ${project.groupId} 64 | dbdeploy-core 65 | ${project.version} 66 | 67 | 68 | commons-cli 69 | commons-cli 70 | 1.2 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /dbdeploy-cli/src/main/java/com/dbdeploy/CommandLineTarget.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy; 2 | 3 | import com.dbdeploy.exceptions.UsageException; 4 | import com.dbdeploy.DbDeploy; 5 | 6 | public class CommandLineTarget { 7 | private static final DbDeployCommandLineParser commandLineParser = new DbDeployCommandLineParser(); 8 | 9 | public static void main(String[] args) { 10 | try { 11 | DbDeploy dbDeploy = new DbDeploy(); 12 | commandLineParser.parse(args, dbDeploy); 13 | dbDeploy.go(); 14 | } catch (UsageException ex) { 15 | System.err.println("ERROR: " + ex.getMessage()); 16 | commandLineParser.printUsage(); 17 | } catch (Exception ex) { 18 | System.err.println("Failed to apply changes: " + ex); 19 | ex.printStackTrace(); 20 | System.exit(2); 21 | } 22 | 23 | System.exit(0); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /dbdeploy-cli/src/main/java/com/dbdeploy/DbDeployCommandLineParser.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy; 2 | 3 | import com.dbdeploy.database.LineEnding; 4 | import com.dbdeploy.exceptions.UsageException; 5 | import com.dbdeploy.database.DelimiterType; 6 | import org.apache.commons.cli.*; 7 | 8 | import java.beans.BeanInfo; 9 | import java.beans.Introspector; 10 | import java.beans.PropertyDescriptor; 11 | import java.io.File; 12 | 13 | public class DbDeployCommandLineParser { 14 | private final UserInputReader userInputReader; 15 | 16 | public DbDeployCommandLineParser() { 17 | this(new UserInputReader()); 18 | } 19 | 20 | public DbDeployCommandLineParser(UserInputReader userInputReader) { 21 | this.userInputReader = userInputReader; 22 | } 23 | 24 | public void parse(String[] args, DbDeploy dbDeploy) throws UsageException { 25 | try { 26 | dbDeploy.setScriptdirectory(new File(".")); 27 | final CommandLine commandLine = new GnuParser().parse(getOptions(), args); 28 | copyValuesFromCommandLineToDbDeployBean(dbDeploy, commandLine); 29 | 30 | if (commandLine.hasOption("password") && commandLine.getOptionValue("password") == null) { 31 | dbDeploy.setPassword(userInputReader.read("Password")); 32 | } 33 | } catch (ParseException e) { 34 | throw new UsageException(e.getMessage(), e); 35 | } 36 | } 37 | 38 | private void copyValuesFromCommandLineToDbDeployBean(DbDeploy dbDeploy, CommandLine commandLine) { 39 | 40 | try { 41 | final BeanInfo info = Introspector.getBeanInfo(dbDeploy.getClass()); 42 | 43 | for (PropertyDescriptor p : info.getPropertyDescriptors()) { 44 | final String propertyName = p.getDisplayName(); 45 | if (commandLine.hasOption(propertyName)) { 46 | Object value = commandLine.getOptionValue(propertyName); 47 | if (p.getPropertyType().isAssignableFrom(File.class)) { 48 | value = new File((String) value); 49 | } 50 | 51 | p.getWriteMethod().invoke(dbDeploy, value); 52 | } 53 | } 54 | 55 | if (commandLine.hasOption("delimitertype")) { 56 | dbDeploy.setDelimiterType(DelimiterType.valueOf(commandLine.getOptionValue("delimitertype"))); 57 | } 58 | 59 | if (commandLine.hasOption("lineending")) { 60 | dbDeploy.setLineEnding(LineEnding.valueOf(commandLine.getOptionValue("lineending"))); 61 | } 62 | 63 | } catch (Exception e) { 64 | throw new RuntimeException(e); 65 | } 66 | } 67 | 68 | public void printUsage() { 69 | HelpFormatter formatter = new HelpFormatter(); 70 | formatter.printHelp("dbdeploy", getOptions()); 71 | 72 | } 73 | 74 | @SuppressWarnings({"AccessStaticViaInstance"}) 75 | private Options getOptions() { 76 | final Options options = new Options(); 77 | 78 | options.addOption(OptionBuilder 79 | .hasArg() 80 | .withDescription("database user id") 81 | .withLongOpt("userid") 82 | .create("U")); 83 | 84 | options.addOption(OptionBuilder 85 | .hasOptionalArg() 86 | .withDescription("database password (use -P without a argument value to be prompted)") 87 | .withLongOpt("password") 88 | .create("P")); 89 | 90 | options.addOption(OptionBuilder 91 | .hasArg() 92 | .withDescription("database driver class") 93 | .withLongOpt("driver") 94 | .create("D")); 95 | 96 | options.addOption(OptionBuilder 97 | .hasArg() 98 | .withDescription("database url") 99 | .withLongOpt("url") 100 | .create("u")); 101 | 102 | options.addOption(OptionBuilder 103 | .hasArg() 104 | .withDescription("directory containing change scripts (default: .)") 105 | .withLongOpt("scriptdirectory") 106 | .create("s")); 107 | 108 | options.addOption(OptionBuilder 109 | .hasArg() 110 | .withDescription("encoding for input and output files (default: UTF-8)") 111 | .withLongOpt("encoding") 112 | .create("e")); 113 | 114 | options.addOption(OptionBuilder 115 | .hasArg() 116 | .withDescription("output file") 117 | .withLongOpt("outputfile") 118 | .create("o")); 119 | 120 | options.addOption(OptionBuilder 121 | .hasArg() 122 | .withDescription("dbms type") 123 | .withLongOpt("dbms") 124 | .create("d")); 125 | 126 | options.addOption(OptionBuilder 127 | .hasArg() 128 | .withDescription("template directory") 129 | .withLongOpt("templatedir") 130 | .create()); 131 | 132 | options.addOption(OptionBuilder 133 | .hasArg() 134 | .withDescription("name of change log table to use (default: changelog)") 135 | .withLongOpt("changeLogTableName") 136 | .create("t")); 137 | 138 | options.addOption(OptionBuilder 139 | .hasArg() 140 | .withDescription("delimiter to separate sql statements") 141 | .withLongOpt("delimiter") 142 | .create()); 143 | 144 | options.addOption(OptionBuilder 145 | .hasArg() 146 | .withDescription("delimiter type to separate sql statements (row or normal)") 147 | .withLongOpt("delimitertype") 148 | .create()); 149 | 150 | options.addOption(OptionBuilder 151 | .hasArg() 152 | .withDescription("line ending to use when applying scripts direct to db (platform, cr, crlf, lf)") 153 | .withLongOpt("lineending") 154 | .create()); 155 | 156 | 157 | return options; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /dbdeploy-cli/src/main/java/com/dbdeploy/UserInputReader.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy; 2 | 3 | import java.util.Scanner; 4 | 5 | public class UserInputReader { 6 | public String read(String prompt) { 7 | System.out.print(prompt + ": "); 8 | Scanner scanner = new Scanner(System.in); 9 | return scanner.nextLine(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /dbdeploy-cli/src/test/java/com/dbdeploy/DbDeployCommandLineParserTest.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy; 2 | 3 | import com.dbdeploy.database.DelimiterType; 4 | import com.dbdeploy.database.LineEnding; 5 | import org.junit.Test; 6 | 7 | import java.io.File; 8 | 9 | import static org.hamcrest.Matchers.is; 10 | import static org.junit.Assert.assertEquals; 11 | import static org.junit.Assert.assertThat; 12 | import static org.mockito.Mockito.*; 13 | 14 | public class DbDeployCommandLineParserTest { 15 | UserInputReader userInputReader = mock(UserInputReader.class); 16 | 17 | private final DbDeploy dbDeploy = new DbDeploy(); 18 | private final DbDeployCommandLineParser parser = new DbDeployCommandLineParser(userInputReader); 19 | 20 | @Test 21 | public void canParseUserIdFromCommandLine() throws Exception { 22 | parser.parse("-U myuserid".split(" "), dbDeploy); 23 | assertEquals("myuserid", dbDeploy.getUserid()); 24 | } 25 | 26 | @Test 27 | public void thisIsntReallyATestBecuaseThereIsNoAssertButItsVeryUsefulToLookAtTheResult() throws Exception { 28 | parser.printUsage(); 29 | } 30 | 31 | @Test 32 | public void checkAllOfTheOtherFieldsParseOkHere() throws Exception { 33 | parser.parse(("-U userid -Ppassword --driver a.b.c --url b:c:d " + 34 | "--scriptdirectory . -o output.sql " + 35 | "--changeLogTableName my-change-log " + 36 | "--dbms ora " + 37 | "--templatedir /tmp/mytemplates " + 38 | "--delimiter \\ --delimitertype row").split(" "), dbDeploy); 39 | 40 | assertThat(dbDeploy.getUserid(), is("userid")); 41 | assertThat(dbDeploy.getPassword(), is("password")); 42 | assertThat(dbDeploy.getDriver(), is("a.b.c")); 43 | assertThat(dbDeploy.getUrl(), is("b:c:d")); 44 | assertThat(dbDeploy.getScriptdirectory().getName(), is(".")); 45 | assertThat(dbDeploy.getOutputfile().getName(), is("output.sql")); 46 | assertThat(dbDeploy.getDbms(), is("ora")); 47 | assertThat(dbDeploy.getChangeLogTableName(), is("my-change-log")); 48 | assertThat(dbDeploy.getDelimiter(), is("\\")); 49 | assertThat(dbDeploy.getDelimiterType(), is(DelimiterType.row)); 50 | assertThat(dbDeploy.getTemplatedir().getPath(), is(File.separator + "tmp" + File.separator + "mytemplates")); 51 | } 52 | 53 | @Test 54 | public void delimiterTypeWorksOk() throws Exception { 55 | parser.parse("--delimitertype normal".split(" "), dbDeploy); 56 | assertThat(dbDeploy.getDelimiterType(), is(DelimiterType.normal)); 57 | 58 | parser.parse("--delimitertype row".split(" "), dbDeploy); 59 | assertThat(dbDeploy.getDelimiterType(), is(DelimiterType.row)); 60 | } 61 | 62 | @Test 63 | public void lineEndingWorksOk() throws Exception { 64 | assertThat(dbDeploy.getLineEnding(), is(LineEnding.platform)); 65 | 66 | parser.parse("--lineending cr".split(" "), dbDeploy); 67 | assertThat(dbDeploy.getLineEnding(), is(LineEnding.cr)); 68 | 69 | parser.parse("--lineending crlf".split(" "), dbDeploy); 70 | assertThat(dbDeploy.getLineEnding(), is(LineEnding.crlf)); 71 | 72 | parser.parse("--lineending lf".split(" "), dbDeploy); 73 | assertThat(dbDeploy.getLineEnding(), is(LineEnding.lf)); 74 | 75 | parser.parse("--lineending platform".split(" "), dbDeploy); 76 | assertThat(dbDeploy.getLineEnding(), is(LineEnding.platform)); 77 | 78 | } 79 | 80 | @Test 81 | public void shouldPromptFromStdinForPasswordIfPasswordParamSuppliedWithNoArg() throws Exception { 82 | when(userInputReader.read("Password")).thenReturn("user entered password"); 83 | 84 | parser.parse(new String[] { "-P" }, dbDeploy); 85 | 86 | assertThat(dbDeploy.getPassword(), is("user entered password")); 87 | } 88 | 89 | @Test 90 | public void shouldNotPromptForPasswordWhenSupplied() throws Exception { 91 | parser.parse(new String[]{"-P", "password"}, dbDeploy); 92 | verifyZeroInteractions(userInputReader); 93 | } 94 | 95 | @Test 96 | public void shouldNotPromptForPasswordNotSpecifiedOnCommandLine() throws Exception { 97 | // this is important: not all databases require passwords :) 98 | parser.parse(new String[] {}, dbDeploy); 99 | verifyZeroInteractions(userInputReader); 100 | } 101 | 102 | } 103 | 104 | -------------------------------------------------------------------------------- /dbdeploy-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | com.dbdeploy 6 | dbdeploy-parent 7 | 3.0-SNAPSHOT 8 | 9 | 10 | dbdeploy-core 11 | jar 12 | dbdeploy-core 13 | 14 | 15 | 16 | 17 | org.apache.maven.plugins 18 | maven-source-plugin 19 | 20 | 21 | package 22 | 23 | jar 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | hsqldb 34 | hsqldb 35 | ${hsqldb.version} 36 | test 37 | 38 | 39 | commons-io 40 | commons-io 41 | 1.4 42 | test 43 | 44 | 45 | commons-lang 46 | commons-lang 47 | 2.4 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /dbdeploy-core/src/it/db/deltas/001_create_test_table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE Test (id INTEGER); 2 | 3 | -------------------------------------------------------------------------------- /dbdeploy-core/src/it/db/deltas/002_insert_value_into_test_table.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO Test VALUES (6); 2 | -------------------------------------------------------------------------------- /dbdeploy-core/src/it/db/high_numbers/1000_high_number.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- This sql file has a number > 1000 - see http://code.google.com/p/dbdeploy/issues/detail?id=36 3 | -- 4 | -------------------------------------------------------------------------------- /dbdeploy-core/src/it/db/invalid_deltas/001_create_test_table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE Test (id INTEGER); 2 | 3 | -------------------------------------------------------------------------------- /dbdeploy-core/src/it/db/invalid_deltas/002_insert_value_into_test_table.sql: -------------------------------------------------------------------------------- 1 | -- this is deliberately invalid 2 | INSERT INTO Test VALUES (1,2,3,4); 3 | -------------------------------------------------------------------------------- /dbdeploy-core/src/it/db/multi_statement_deltas/001_create_test_table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE Test (id INTEGER); 2 | 3 | -------------------------------------------------------------------------------- /dbdeploy-core/src/it/db/multi_statement_deltas/002_insert_values_into_test_table.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO Test VALUES (6); 2 | INSERT INTO Test VALUES (7); 3 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/AppliedChangesProvider.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy; 2 | 3 | import java.util.List; 4 | 5 | public interface AppliedChangesProvider { 6 | List getAppliedChanges(); 7 | } 8 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/AvailableChangeScriptsProvider.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy; 2 | 3 | import com.dbdeploy.scripts.ChangeScript; 4 | 5 | import java.util.List; 6 | 7 | public interface AvailableChangeScriptsProvider { 8 | List getAvailableChangeScripts(); 9 | } 10 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/ChangeScriptApplier.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy; 2 | 3 | import com.dbdeploy.scripts.ChangeScript; 4 | 5 | import java.util.List; 6 | 7 | public interface ChangeScriptApplier { 8 | void apply(List changeScript); 9 | } 10 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/Controller.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy; 2 | 3 | import com.dbdeploy.exceptions.DbDeployException; 4 | import com.dbdeploy.scripts.ChangeScript; 5 | 6 | import java.io.IOException; 7 | import java.util.ArrayList; 8 | import java.util.Collections; 9 | import java.util.List; 10 | 11 | public class Controller { 12 | 13 | private final AvailableChangeScriptsProvider availableChangeScriptsProvider; 14 | private final AppliedChangesProvider appliedChangesProvider; 15 | private final ChangeScriptApplier changeScriptApplier; 16 | private final ChangeScriptApplier undoScriptApplier; 17 | 18 | private final PrettyPrinter prettyPrinter = new PrettyPrinter(); 19 | 20 | public Controller(AvailableChangeScriptsProvider availableChangeScriptsProvider, 21 | AppliedChangesProvider appliedChangesProvider, 22 | ChangeScriptApplier changeScriptApplier, ChangeScriptApplier undoScriptApplier) { 23 | this.availableChangeScriptsProvider = availableChangeScriptsProvider; 24 | this.appliedChangesProvider = appliedChangesProvider; 25 | this.changeScriptApplier = changeScriptApplier; 26 | this.undoScriptApplier = undoScriptApplier; 27 | } 28 | 29 | public void processChangeScripts(Long lastChangeToApply) throws DbDeployException, IOException { 30 | if (lastChangeToApply != Long.MAX_VALUE) { 31 | info("Only applying changes up and including change script #" + lastChangeToApply); 32 | } 33 | 34 | List scripts = availableChangeScriptsProvider.getAvailableChangeScripts(); 35 | List applied = appliedChangesProvider.getAppliedChanges(); 36 | List toApply = identifyChangesToApply(lastChangeToApply, scripts, applied); 37 | 38 | logStatus(scripts, applied, toApply); 39 | 40 | changeScriptApplier.apply(Collections.unmodifiableList(toApply)); 41 | 42 | if (undoScriptApplier != null) { 43 | info("Generating undo scripts..."); 44 | Collections.reverse(toApply); 45 | undoScriptApplier.apply(Collections.unmodifiableList(toApply)); 46 | } 47 | } 48 | 49 | private void logStatus(List scripts, List applied, List toApply) { 50 | info("Changes currently applied to database:\n " + prettyPrinter.format(applied)); 51 | info("Scripts available:\n " + prettyPrinter.formatChangeScriptList(scripts)); 52 | info("To be applied:\n " + prettyPrinter.formatChangeScriptList(toApply)); 53 | } 54 | 55 | private List identifyChangesToApply(Long lastChangeToApply, List scripts, List applied) { 56 | List result = new ArrayList(); 57 | 58 | for (ChangeScript script : scripts) { 59 | if (script.getId() > lastChangeToApply) 60 | break; 61 | 62 | if (!applied.contains(script.getId())) { 63 | result.add(script); 64 | } 65 | } 66 | 67 | return result; 68 | } 69 | 70 | private void info(String string) { 71 | System.err.println(string); 72 | } 73 | } -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/DbDeploy.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy; 2 | 3 | import com.dbdeploy.appliers.DirectToDbApplier; 4 | import com.dbdeploy.appliers.TemplateBasedApplier; 5 | import com.dbdeploy.appliers.UndoTemplateBasedApplier; 6 | import com.dbdeploy.database.DelimiterType; 7 | import com.dbdeploy.database.LineEnding; 8 | import com.dbdeploy.database.QueryStatementSplitter; 9 | import com.dbdeploy.database.changelog.DatabaseSchemaVersionManager; 10 | import com.dbdeploy.database.changelog.QueryExecuter; 11 | import com.dbdeploy.exceptions.UsageException; 12 | import com.dbdeploy.scripts.ChangeScriptRepository; 13 | import com.dbdeploy.scripts.DirectoryScanner; 14 | 15 | import java.io.File; 16 | import java.io.PrintWriter; 17 | 18 | public class DbDeploy { 19 | private String url; 20 | private String userid; 21 | private String password; 22 | private String encoding = "UTF-8"; 23 | private File scriptdirectory; 24 | private File outputfile; 25 | private File undoOutputfile; 26 | private LineEnding lineEnding = LineEnding.platform; 27 | private String dbms; 28 | private Long lastChangeToApply = Long.MAX_VALUE; 29 | private String driver; 30 | private String changeLogTableName = "changelog"; 31 | private String delimiter = ";"; 32 | private DelimiterType delimiterType = DelimiterType.normal; 33 | private File templatedir; 34 | 35 | public void setDriver(String driver) { 36 | this.driver = driver; 37 | } 38 | 39 | public void setUrl(String url) { 40 | this.url = url; 41 | } 42 | 43 | public void setUserid(String userid) { 44 | this.userid = userid; 45 | } 46 | 47 | public void setPassword(String password) { 48 | this.password = password; 49 | } 50 | 51 | public void setScriptdirectory(File scriptdirectory) { 52 | this.scriptdirectory = scriptdirectory; 53 | } 54 | 55 | public void setOutputfile(File outputfile) { 56 | this.outputfile = outputfile; 57 | } 58 | 59 | public void setDbms(String dbms) { 60 | this.dbms = dbms; 61 | } 62 | 63 | public void setLastChangeToApply(Long lastChangeToApply) { 64 | this.lastChangeToApply = lastChangeToApply; 65 | } 66 | 67 | public void setUndoOutputfile(File undoOutputfile) { 68 | this.undoOutputfile = undoOutputfile; 69 | } 70 | 71 | public void setChangeLogTableName(String changeLogTableName) { 72 | this.changeLogTableName = changeLogTableName; 73 | } 74 | 75 | public void setEncoding(String encoding) { 76 | this.encoding = encoding; 77 | } 78 | 79 | public void setLineEnding(LineEnding lineEnding) { 80 | this.lineEnding = lineEnding; 81 | } 82 | 83 | public void go() throws Exception { 84 | System.err.println(getWelcomeString()); 85 | 86 | validate(); 87 | 88 | Class.forName(driver); 89 | 90 | QueryExecuter queryExecuter = new QueryExecuter(url, userid, password); 91 | 92 | DatabaseSchemaVersionManager databaseSchemaVersionManager = 93 | new DatabaseSchemaVersionManager(queryExecuter, changeLogTableName); 94 | 95 | ChangeScriptRepository changeScriptRepository = 96 | new ChangeScriptRepository(new DirectoryScanner(encoding).getChangeScriptsForDirectory(scriptdirectory)); 97 | 98 | ChangeScriptApplier doScriptApplier; 99 | 100 | if (outputfile != null) { 101 | doScriptApplier = new TemplateBasedApplier( 102 | new PrintWriter(outputfile, encoding), dbms, 103 | changeLogTableName, delimiter, delimiterType, getTemplatedir()); 104 | } else { 105 | QueryStatementSplitter splitter = new QueryStatementSplitter(); 106 | splitter.setDelimiter(getDelimiter()); 107 | splitter.setDelimiterType(getDelimiterType()); 108 | splitter.setOutputLineEnding(lineEnding); 109 | doScriptApplier = new DirectToDbApplier(queryExecuter, databaseSchemaVersionManager, splitter); 110 | } 111 | 112 | ChangeScriptApplier undoScriptApplier = null; 113 | 114 | if (undoOutputfile != null) { 115 | undoScriptApplier = new UndoTemplateBasedApplier( 116 | new PrintWriter(undoOutputfile), dbms, changeLogTableName, delimiter, delimiterType, templatedir); 117 | 118 | } 119 | 120 | Controller controller = new Controller(changeScriptRepository, databaseSchemaVersionManager, doScriptApplier, undoScriptApplier); 121 | 122 | controller.processChangeScripts(lastChangeToApply); 123 | 124 | queryExecuter.close(); 125 | } 126 | 127 | private void validate() throws UsageException { 128 | checkForRequiredParameter(userid, "userid"); 129 | checkForRequiredParameter(driver, "driver"); 130 | checkForRequiredParameter(url, "url"); 131 | checkForRequiredParameter(scriptdirectory, "dir"); 132 | 133 | if (scriptdirectory == null || !scriptdirectory.isDirectory()) { 134 | throw new UsageException("Script directory must point to a valid directory"); 135 | } 136 | } 137 | 138 | private void checkForRequiredParameter(String parameterValue, String parameterName) throws UsageException { 139 | if (parameterValue == null || parameterValue.length() == 0) { 140 | UsageException.throwForMissingRequiredValue(parameterName); 141 | } 142 | } 143 | 144 | private void checkForRequiredParameter(Object parameterValue, String parameterName) throws UsageException { 145 | if (parameterValue == null) { 146 | UsageException.throwForMissingRequiredValue(parameterName); 147 | } 148 | } 149 | 150 | public String getUserid() { 151 | return userid; 152 | } 153 | 154 | public String getUrl() { 155 | return url; 156 | } 157 | 158 | public String getPassword() { 159 | return password; 160 | } 161 | 162 | public File getScriptdirectory() { 163 | return scriptdirectory; 164 | } 165 | 166 | public File getOutputfile() { 167 | return outputfile; 168 | } 169 | 170 | public File getUndoOutputfile() { 171 | return undoOutputfile; 172 | } 173 | 174 | public String getDbms() { 175 | return dbms; 176 | } 177 | 178 | public Long getLastChangeToApply() { 179 | return lastChangeToApply; 180 | } 181 | 182 | public String getDriver() { 183 | return driver; 184 | } 185 | 186 | public void setTemplatedir(File templatedir) { 187 | this.templatedir = templatedir; 188 | } 189 | 190 | public File getTemplatedir() { 191 | return templatedir; 192 | } 193 | 194 | public String getChangeLogTableName() { 195 | return changeLogTableName; 196 | } 197 | 198 | public String getDelimiter() { 199 | return delimiter; 200 | } 201 | 202 | public void setDelimiter(String delimiter) { 203 | this.delimiter = delimiter; 204 | } 205 | 206 | public DelimiterType getDelimiterType() { 207 | return delimiterType; 208 | } 209 | 210 | 211 | public void setDelimiterType(DelimiterType delimiterType) { 212 | this.delimiterType = delimiterType; 213 | } 214 | 215 | public String getWelcomeString() { 216 | String version = getClass().getPackage().getImplementationVersion(); 217 | return "dbdeploy " + version; 218 | } 219 | 220 | public String getEncoding() { 221 | return encoding; 222 | } 223 | 224 | public LineEnding getLineEnding() { 225 | return lineEnding; 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/PrettyPrinter.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy; 2 | 3 | import com.dbdeploy.scripts.ChangeScript; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class PrettyPrinter { 9 | 10 | public String format(List appliedChanges) { 11 | if (appliedChanges.isEmpty()) { 12 | return "(none)"; 13 | } 14 | 15 | StringBuilder builder = new StringBuilder(); 16 | 17 | Long lastRangeStart = null; 18 | Long lastNumber = null; 19 | 20 | for (Long thisNumber : appliedChanges) { 21 | if (lastNumber == null) { 22 | // first in loop 23 | lastNumber = thisNumber; 24 | lastRangeStart = thisNumber; 25 | } else if (thisNumber == lastNumber + 1) { 26 | // continuation of current range 27 | lastNumber = thisNumber; 28 | } else { 29 | // doesn't fit into last range - so output the old range and 30 | // start a new one 31 | appendRange(builder, lastRangeStart, lastNumber); 32 | lastNumber = thisNumber; 33 | lastRangeStart = thisNumber; 34 | } 35 | } 36 | 37 | appendRange(builder, lastRangeStart, lastNumber); 38 | return builder.toString(); 39 | } 40 | 41 | private void appendRange(StringBuilder builder, Long lastRangeStart, Long lastNumber) { 42 | if (lastRangeStart == lastNumber) { 43 | appendWithPossibleComma(builder, lastNumber); 44 | } else if (lastRangeStart + 1 == lastNumber) { 45 | appendWithPossibleComma(builder, lastRangeStart); 46 | appendWithPossibleComma(builder, lastNumber); 47 | } else { 48 | appendWithPossibleComma(builder, lastRangeStart + ".." + lastNumber); 49 | } 50 | } 51 | 52 | private void appendWithPossibleComma(StringBuilder builder, Object o) { 53 | if (builder.length() != 0) { 54 | builder.append(", "); 55 | } 56 | builder.append(o); 57 | } 58 | 59 | public String formatChangeScriptList(List changeScripts) { 60 | List numberList = new ArrayList(changeScripts.size()); 61 | 62 | for (ChangeScript changeScript : changeScripts) { 63 | numberList.add(changeScript.getId()); 64 | } 65 | 66 | return format(numberList); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/appliers/ApplyMode.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.appliers; 2 | 3 | public enum ApplyMode { 4 | DO, 5 | UNDO, 6 | } 7 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/appliers/DirectToDbApplier.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.appliers; 2 | 3 | import com.dbdeploy.ChangeScriptApplier; 4 | import com.dbdeploy.database.QueryStatementSplitter; 5 | import com.dbdeploy.database.changelog.DatabaseSchemaVersionManager; 6 | import com.dbdeploy.database.changelog.QueryExecuter; 7 | import com.dbdeploy.exceptions.ChangeScriptFailedException; 8 | import com.dbdeploy.scripts.ChangeScript; 9 | 10 | import java.sql.SQLException; 11 | import java.util.List; 12 | 13 | public class DirectToDbApplier implements ChangeScriptApplier { 14 | private final QueryExecuter queryExecuter; 15 | private final DatabaseSchemaVersionManager schemaVersionManager; 16 | private final QueryStatementSplitter splitter; 17 | 18 | public DirectToDbApplier(QueryExecuter queryExecuter, DatabaseSchemaVersionManager schemaVersionManager, QueryStatementSplitter splitter) { 19 | this.queryExecuter = queryExecuter; 20 | this.schemaVersionManager = schemaVersionManager; 21 | this.splitter = splitter; 22 | } 23 | 24 | public void apply(List changeScript) { 25 | begin(); 26 | 27 | for (ChangeScript script : changeScript) { 28 | System.err.println("Applying " + script + "..."); 29 | 30 | applyChangeScript(script); 31 | insertToSchemaVersionTable(script); 32 | 33 | commitTransaction(); 34 | } 35 | } 36 | 37 | public void begin() { 38 | try { 39 | queryExecuter.setAutoCommit(false); 40 | } catch (SQLException e) { 41 | throw new RuntimeException(e); 42 | } 43 | } 44 | 45 | protected void applyChangeScript(ChangeScript script) { 46 | List statements = splitter.split(script.getContent()); 47 | 48 | for (int i = 0; i < statements.size(); i++) { 49 | String statement = statements.get(i); 50 | try { 51 | if (statements.size() > 1) { 52 | System.err.println(" -> statement " + (i+1) + " of " + statements.size() + "..."); 53 | } 54 | queryExecuter.execute(statement); 55 | } catch (SQLException e) { 56 | throw new ChangeScriptFailedException(e, script, i+1, statement); 57 | } 58 | } 59 | } 60 | 61 | protected void insertToSchemaVersionTable(ChangeScript changeScript) { 62 | schemaVersionManager.recordScriptApplied(changeScript); 63 | } 64 | 65 | protected void commitTransaction() { 66 | try { 67 | queryExecuter.commit(); 68 | } catch (SQLException e) { 69 | throw new RuntimeException(); 70 | } 71 | } 72 | 73 | 74 | } 75 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/appliers/TemplateBasedApplier.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.appliers; 2 | 3 | import com.dbdeploy.ChangeScriptApplier; 4 | import com.dbdeploy.database.DelimiterType; 5 | import com.dbdeploy.exceptions.UsageException; 6 | import com.dbdeploy.scripts.ChangeScript; 7 | import freemarker.cache.ClassTemplateLoader; 8 | import freemarker.cache.FileTemplateLoader; 9 | import freemarker.cache.MultiTemplateLoader; 10 | import freemarker.cache.TemplateLoader; 11 | import freemarker.template.Configuration; 12 | import freemarker.template.Template; 13 | 14 | import java.io.*; 15 | import java.util.HashMap; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | 20 | public class TemplateBasedApplier implements ChangeScriptApplier { 21 | private Configuration configuration; 22 | private Writer writer; 23 | private String syntax; 24 | private String changeLogTableName; 25 | private String delimiter; 26 | private DelimiterType delimiterType; 27 | 28 | public TemplateBasedApplier(Writer writer, String syntax, String changeLogTableName, String delimiter, DelimiterType delimiterType, File templateDirectory) throws IOException { 29 | this.syntax = syntax; 30 | this.changeLogTableName = changeLogTableName; 31 | this.delimiter = delimiter; 32 | this.delimiterType = delimiterType; 33 | this.writer = writer; 34 | this.configuration = new Configuration(); 35 | 36 | FileTemplateLoader fileTemplateLoader = createFileTemplateLoader(templateDirectory); 37 | this.configuration.setTemplateLoader( 38 | new MultiTemplateLoader(new TemplateLoader[]{ 39 | fileTemplateLoader, 40 | new ClassTemplateLoader(getClass(), "/"), 41 | })); 42 | } 43 | 44 | private FileTemplateLoader createFileTemplateLoader(File templateDirectory) throws IOException { 45 | if (templateDirectory == null) { 46 | return new FileTemplateLoader(); 47 | } else { 48 | return new FileTemplateLoader(templateDirectory, true); 49 | } 50 | } 51 | 52 | public void apply(List changeScripts) { 53 | String filename = syntax + "_" + getTemplateQualifier() + ".ftl"; 54 | 55 | try { 56 | Map model = new HashMap(); 57 | model.put("scripts", changeScripts); 58 | model.put("changeLogTableName", changeLogTableName); 59 | model.put("delimiter", delimiter); 60 | model.put("separator", delimiterType == DelimiterType.row ? "\n" : ""); 61 | 62 | try { 63 | Template template = configuration.getTemplate(filename); 64 | template.process(model, writer); 65 | } finally { 66 | writer.close(); 67 | } 68 | } catch (FileNotFoundException ex) { 69 | throw new UsageException("Could not find template named " + filename + "\n" + 70 | "Check that you have got the name of the database syntax correct.", ex); 71 | } catch (Exception e) { 72 | throw new RuntimeException(e); 73 | } 74 | } 75 | 76 | protected String getTemplateQualifier() { 77 | return "apply"; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/appliers/UndoTemplateBasedApplier.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.appliers; 2 | 3 | import com.dbdeploy.database.DelimiterType; 4 | 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.io.OutputStream; 8 | import java.io.Writer; 9 | 10 | public class UndoTemplateBasedApplier extends TemplateBasedApplier { 11 | public UndoTemplateBasedApplier(Writer writer, String syntax, 12 | String changeLogTableName, String delimiter, DelimiterType delimiterType, File templateDirectory) throws IOException { 13 | super(writer, syntax, changeLogTableName, delimiter, delimiterType, templateDirectory); 14 | } 15 | 16 | @Override 17 | protected String getTemplateQualifier() { 18 | return "undo"; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/database/DelimiterType.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.database; 2 | 3 | public enum DelimiterType { 4 | /** 5 | * Delimiter is interpreted whenever it appears at the end of a line 6 | */ 7 | normal { 8 | public boolean matches(String line, String delimiter) { 9 | return line.endsWith(delimiter); 10 | } 11 | }, 12 | 13 | /** 14 | * Delimiter must be on a line all to itself 15 | */ 16 | row { 17 | public boolean matches(String line, String delimiter) { 18 | return line.equals(delimiter); 19 | } 20 | }; 21 | 22 | public abstract boolean matches(String line, String delimiter); 23 | } 24 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/database/LineEnding.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.database; 2 | 3 | import org.apache.commons.lang.SystemUtils; 4 | 5 | public enum LineEnding { 6 | platform { public String get() { return SystemUtils.LINE_SEPARATOR; } }, 7 | cr { public String get() { return "\r"; } }, 8 | crlf { public String get() { return "\r\n"; } }, 9 | lf { public String get() { return "\n"; } }; 10 | 11 | abstract public String get(); 12 | } 13 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/database/QueryStatementSplitter.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.database; 2 | 3 | import org.apache.commons.lang.StringUtils; 4 | import org.apache.commons.lang.text.StrBuilder; 5 | import org.apache.commons.lang.text.StrMatcher; 6 | import org.apache.commons.lang.text.StrTokenizer; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class QueryStatementSplitter { 12 | private String delimiter = ";"; 13 | private DelimiterType delimiterType = DelimiterType.normal; 14 | private LineEnding lineEnding = LineEnding.platform; 15 | 16 | public QueryStatementSplitter() { 17 | } 18 | 19 | public List split(String input) { 20 | List statements = new ArrayList(); 21 | StrBuilder currentSql = new StrBuilder(); 22 | 23 | StrTokenizer lineTokenizer = new StrTokenizer(input); 24 | lineTokenizer.setDelimiterMatcher(StrMatcher.charSetMatcher("\r\n")); 25 | 26 | for (String line : lineTokenizer.getTokenArray()) { 27 | String strippedLine = StringUtils.stripEnd(line, null); 28 | if (!currentSql.isEmpty()) { 29 | currentSql.append(lineEnding.get()); 30 | } 31 | 32 | currentSql.append(strippedLine); 33 | 34 | if (delimiterType.matches(strippedLine, delimiter)) { 35 | statements.add(currentSql.substring(0, currentSql.length() - delimiter.length())); 36 | currentSql.clear(); 37 | } 38 | } 39 | 40 | if (!currentSql.isEmpty()) { 41 | statements.add(currentSql.toString()); 42 | } 43 | 44 | return statements; 45 | } 46 | 47 | public String getDelimiter() { 48 | return delimiter; 49 | } 50 | 51 | public void setDelimiter(String delimiter) { 52 | this.delimiter = delimiter; 53 | } 54 | 55 | public DelimiterType getDelimiterType() { 56 | return delimiterType; 57 | } 58 | 59 | public void setDelimiterType(DelimiterType delimiterType) { 60 | this.delimiterType = delimiterType; 61 | } 62 | 63 | public void setOutputLineEnding(LineEnding lineEnding) { 64 | this.lineEnding = lineEnding; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/database/changelog/DatabaseSchemaVersionManager.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.database.changelog; 2 | 3 | import com.dbdeploy.AppliedChangesProvider; 4 | import com.dbdeploy.exceptions.SchemaVersionTrackingException; 5 | import com.dbdeploy.scripts.ChangeScript; 6 | 7 | import java.sql.ResultSet; 8 | import java.sql.SQLException; 9 | import java.sql.Timestamp; 10 | import java.util.ArrayList; 11 | import java.util.Date; 12 | import java.util.List; 13 | 14 | /** 15 | * This class is responsible for all interaction with the changelog table 16 | */ 17 | public class DatabaseSchemaVersionManager implements AppliedChangesProvider { 18 | 19 | private final QueryExecuter queryExecuter; 20 | private final String changeLogTableName; 21 | private CurrentTimeProvider timeProvider = new CurrentTimeProvider(); 22 | 23 | public DatabaseSchemaVersionManager(QueryExecuter queryExecuter, String changeLogTableName) { 24 | this.queryExecuter = queryExecuter; 25 | this.changeLogTableName = changeLogTableName; 26 | } 27 | 28 | public List getAppliedChanges() { 29 | try { 30 | ResultSet rs = queryExecuter.executeQuery( 31 | "SELECT change_number FROM " + changeLogTableName + " ORDER BY change_number"); 32 | 33 | List changeNumbers = new ArrayList(); 34 | 35 | while (rs.next()) { 36 | changeNumbers.add(rs.getLong(1)); 37 | } 38 | 39 | rs.close(); 40 | 41 | return changeNumbers; 42 | } catch (SQLException e) { 43 | throw new SchemaVersionTrackingException("Could not retrieve change log from database because: " 44 | + e.getMessage(), e); 45 | } 46 | } 47 | 48 | public String getChangelogDeleteSql(ChangeScript script) { 49 | return String.format( 50 | "DELETE FROM " + changeLogTableName + " WHERE change_number = %d", 51 | script.getId()); 52 | } 53 | 54 | public void recordScriptApplied(ChangeScript script) { 55 | try { 56 | queryExecuter.execute( 57 | "INSERT INTO " + changeLogTableName + " (change_number, complete_dt, applied_by, description)" + 58 | " VALUES (?, ?, ?, ?)", 59 | script.getId(), 60 | new Timestamp(timeProvider.now().getTime()), 61 | queryExecuter.getDatabaseUsername(), 62 | script.getDescription() 63 | ); 64 | } catch (SQLException e) { 65 | throw new SchemaVersionTrackingException("Could not update change log because: " 66 | + e.getMessage(), e); 67 | } 68 | } 69 | 70 | public void setTimeProvider(CurrentTimeProvider timeProvider) { 71 | this.timeProvider = timeProvider; 72 | } 73 | 74 | public static class CurrentTimeProvider { 75 | 76 | public Date now() { 77 | return new Date(); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/database/changelog/QueryExecuter.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.database.changelog; 2 | 3 | import java.sql.*; 4 | 5 | public class QueryExecuter { 6 | private final Connection connection; 7 | private final String username; 8 | 9 | public QueryExecuter(String connectionString, String username, String password) throws SQLException { 10 | this.username = username; 11 | connection = DriverManager.getConnection(connectionString, username, password); 12 | } 13 | 14 | public ResultSet executeQuery(String sql) throws SQLException { 15 | Statement statement = connection.createStatement(); 16 | return statement.executeQuery(sql); 17 | } 18 | 19 | public void execute(String sql) throws SQLException { 20 | Statement statement = connection.createStatement(); 21 | try { 22 | statement.execute(sql); 23 | } finally { 24 | statement.close(); 25 | } 26 | } 27 | 28 | public void execute(String sql, Object... params) throws SQLException { 29 | PreparedStatement statement = connection.prepareStatement(sql); 30 | try { 31 | for (int i = 0; i < params.length; i++) { 32 | Object param = params[i]; 33 | statement.setObject(i+1, param); 34 | } 35 | statement.execute(); 36 | } finally { 37 | statement.close(); 38 | } 39 | } 40 | 41 | public void close() throws SQLException { 42 | connection.close(); 43 | } 44 | 45 | public void setAutoCommit(boolean autoCommitMode) throws SQLException { 46 | connection.setAutoCommit(autoCommitMode); 47 | } 48 | 49 | public void commit() throws SQLException { 50 | connection.commit(); 51 | } 52 | 53 | public String getDatabaseUsername() { 54 | return username; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/exceptions/ChangeScriptFailedException.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.exceptions; 2 | 3 | import com.dbdeploy.scripts.ChangeScript; 4 | 5 | import java.sql.SQLException; 6 | 7 | public class ChangeScriptFailedException extends DbDeployException { 8 | private final ChangeScript script; 9 | private final int statement; 10 | private final String executedSql; 11 | 12 | public ChangeScriptFailedException(SQLException cause, ChangeScript script, 13 | int statement, String executedSql) { 14 | super(cause); 15 | this.script = script; 16 | this.statement = statement; 17 | this.executedSql = executedSql; 18 | } 19 | 20 | public ChangeScript getScript() { 21 | return script; 22 | } 23 | 24 | public String getExecutedSql() { 25 | return executedSql; 26 | } 27 | 28 | public int getStatement() { 29 | return statement; 30 | } 31 | 32 | @Override 33 | public String getMessage() { 34 | return "change script " + script + 35 | " failed while executing statement " + statement + ":\n" 36 | + executedSql + "\n -> " + getCause().getMessage(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/exceptions/DbDeployException.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.exceptions; 2 | 3 | public class DbDeployException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public DbDeployException() { 8 | super(); 9 | 10 | } 11 | 12 | public DbDeployException(String message) { 13 | super(message); 14 | 15 | } 16 | 17 | public DbDeployException(String message, Throwable cause) { 18 | super(message, cause); 19 | 20 | } 21 | 22 | public DbDeployException(Throwable cause) { 23 | super(cause); 24 | 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/exceptions/DuplicateChangeScriptException.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.exceptions; 2 | 3 | public class DuplicateChangeScriptException extends DbDeployException { 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public DuplicateChangeScriptException() { 8 | super(); 9 | } 10 | 11 | public DuplicateChangeScriptException(String message, Throwable cause) { 12 | super(message, cause); 13 | } 14 | 15 | public DuplicateChangeScriptException(String message) { 16 | super(message); 17 | } 18 | 19 | public DuplicateChangeScriptException(Throwable cause) { 20 | super(cause); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/exceptions/SchemaVersionTrackingException.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.exceptions; 2 | 3 | public class SchemaVersionTrackingException extends DbDeployException { 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public SchemaVersionTrackingException() { 8 | super(); 9 | 10 | } 11 | 12 | public SchemaVersionTrackingException(String message, Throwable cause) { 13 | super(message, cause); 14 | 15 | } 16 | 17 | public SchemaVersionTrackingException(String message) { 18 | super(message); 19 | 20 | } 21 | 22 | public SchemaVersionTrackingException(Throwable cause) { 23 | super(cause); 24 | 25 | } 26 | 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/exceptions/UnrecognisedFilenameException.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.exceptions; 2 | 3 | public class UnrecognisedFilenameException extends DbDeployException { 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public UnrecognisedFilenameException() { 8 | super(); 9 | 10 | } 11 | 12 | public UnrecognisedFilenameException(String message, Throwable cause) { 13 | super(message, cause); 14 | 15 | } 16 | 17 | public UnrecognisedFilenameException(String message) { 18 | super(message); 19 | 20 | } 21 | 22 | public UnrecognisedFilenameException(Throwable cause) { 23 | super(cause); 24 | 25 | } 26 | 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/exceptions/UsageException.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.exceptions; 2 | 3 | public class UsageException extends DbDeployException { 4 | 5 | public UsageException(String message) { 6 | super(message); 7 | } 8 | 9 | public UsageException(String message, Throwable throwable) { 10 | super(message, throwable); 11 | } 12 | 13 | public static void throwForMissingRequiredValue(String valueName) throws UsageException { 14 | throw new UsageException(valueName + " required"); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/scripts/ChangeScript.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.scripts; 2 | 3 | import com.dbdeploy.exceptions.DbDeployException; 4 | 5 | import java.io.*; 6 | 7 | public class ChangeScript implements Comparable { 8 | 9 | private final long id; 10 | private final File file; 11 | private final String description; 12 | private final String encoding; 13 | private static final String UNDO_MARKER = "--//@UNDO"; 14 | 15 | public ChangeScript(long id) { 16 | this(id, "test"); 17 | } 18 | 19 | public ChangeScript(long id, String description) { 20 | this.id = id; 21 | this.file = null; 22 | this.description = description; 23 | this.encoding = "UTF-8"; 24 | } 25 | 26 | public ChangeScript(long id, File file, String encoding) { 27 | this.id = id; 28 | this.file = file; 29 | this.description = file.getName(); 30 | this.encoding = encoding; 31 | } 32 | 33 | public File getFile() { 34 | return file; 35 | } 36 | 37 | public long getId() { 38 | return id; 39 | } 40 | 41 | public String getDescription() { 42 | return description; 43 | } 44 | 45 | public int compareTo(Object o) { 46 | ChangeScript other = (ChangeScript) o; 47 | return Long.valueOf(this.id).compareTo(other.id); 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return "#" + id + ": " + description; 53 | } 54 | 55 | public String getContent() { 56 | return getFileContents(false); 57 | } 58 | 59 | public String getUndoContent() { 60 | return getFileContents(true); 61 | } 62 | 63 | private String getFileContents(boolean onlyAfterUndoMarker) { 64 | try { 65 | StringBuilder content = new StringBuilder(); 66 | boolean foundUndoMarker = false; 67 | BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), encoding)); 68 | 69 | try { 70 | for (;;) { 71 | String str = reader.readLine(); 72 | 73 | if (str == null) 74 | break; 75 | 76 | if (str.trim().equals(UNDO_MARKER)) { 77 | foundUndoMarker = true; 78 | continue; 79 | } 80 | 81 | if (foundUndoMarker == onlyAfterUndoMarker) { 82 | content.append(str); 83 | content.append('\n'); 84 | } 85 | } 86 | } finally { 87 | reader.close(); 88 | } 89 | 90 | return content.toString(); 91 | } catch (IOException e) { 92 | throw new DbDeployException("Failed to read change script file", e); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/scripts/ChangeScriptCreator.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.scripts; 2 | 3 | import com.dbdeploy.exceptions.UsageException; 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.text.DateFormat; 7 | import java.text.SimpleDateFormat; 8 | import java.util.Date; 9 | 10 | /** 11 | * Responsible for creating a new change script file 12 | * to be used by dbdeploy. This class will generate 13 | * a new change script using a timestamp as the change 14 | * script number and any supplied text as the rest 15 | * of the filename. 16 | * 17 | * @author jbogan 18 | */ 19 | public class ChangeScriptCreator { 20 | private String changeScriptSuffix = ".sql"; 21 | private String changeScriptTimestampFormat = "yyyyMMddHHmmss"; 22 | private String scriptDescription; 23 | private File scriptDirectory; 24 | private DateFormat dateFormatter; 25 | 26 | public ChangeScriptCreator() { 27 | dateFormatter = new SimpleDateFormat(changeScriptTimestampFormat); 28 | } 29 | 30 | public File go() throws IOException { 31 | validate(); 32 | 33 | return createScript(); 34 | } 35 | 36 | private void validate() { 37 | if (scriptDirectory == null || !scriptDirectory.isDirectory()) { 38 | throw new UsageException("Script directory must point to a valid directory"); 39 | } 40 | } 41 | 42 | public File createScript() throws IOException { 43 | final String newScriptFileName = getChangeScriptFileName(); 44 | final String fullScriptPath = scriptDirectory + File.separator + newScriptFileName; 45 | 46 | final File newChangeScriptFile = new File(fullScriptPath); 47 | if (newChangeScriptFile.createNewFile()) { 48 | return newChangeScriptFile; 49 | } else { 50 | throw new IOException("Unable to create new change script " + fullScriptPath); 51 | } 52 | } 53 | 54 | private String getChangeScriptFileName() { 55 | final StringBuilder fileNameBuilder = new StringBuilder(); 56 | fileNameBuilder.append(getFileTimestamp()); 57 | if (scriptDescription != null && !scriptDescription.equals("")) { 58 | fileNameBuilder.append("_"); 59 | fileNameBuilder.append(scriptDescription); 60 | } 61 | fileNameBuilder.append(changeScriptSuffix); 62 | 63 | return fileNameBuilder.toString(); 64 | } 65 | 66 | private String getFileTimestamp() { 67 | return dateFormatter.format(new Date()); 68 | } 69 | 70 | public void setScriptDescription(final String scriptDescription) { 71 | this.scriptDescription = scriptDescription; 72 | } 73 | 74 | public void setScriptDirectory(final File scriptDirectory) { 75 | this.scriptDirectory = scriptDirectory; 76 | } 77 | 78 | public static void main(String[] args) { 79 | ChangeScriptCreator creator = new ChangeScriptCreator(); 80 | 81 | try { 82 | parseArguments(args, creator); 83 | creator.go(); 84 | } catch (UsageException ex) { 85 | System.err.println("ERROR: " + ex.getMessage()); 86 | System.err.println("Usage: java " + creator.getClass().getName() + " scriptDirectory [scriptName]"); 87 | } catch (Exception ex) { 88 | System.err.println("Failed to create script: " + ex); 89 | ex.printStackTrace(); 90 | System.exit(2); 91 | } 92 | 93 | System.exit(0); 94 | } 95 | 96 | private static void parseArguments(String[] args, ChangeScriptCreator creator) { 97 | if (args.length >= 1) { 98 | final String scriptDirectoryPath = args[0]; 99 | creator.setScriptDirectory(new File(scriptDirectoryPath)); 100 | } 101 | 102 | if (args.length >= 2) { 103 | final String scriptDescription = args[1]; 104 | creator.setScriptDescription(scriptDescription); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/scripts/ChangeScriptRepository.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.scripts; 2 | 3 | import com.dbdeploy.exceptions.DuplicateChangeScriptException; 4 | import com.dbdeploy.AvailableChangeScriptsProvider; 5 | 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | 10 | public class ChangeScriptRepository implements AvailableChangeScriptsProvider { 11 | 12 | private final List scripts; 13 | 14 | @SuppressWarnings("unchecked") 15 | public ChangeScriptRepository(List scripts) throws DuplicateChangeScriptException { 16 | this.scripts = scripts; 17 | 18 | Collections.sort(this.scripts); 19 | 20 | checkForDuplicateIds(scripts); 21 | } 22 | 23 | private void checkForDuplicateIds(List scripts) throws DuplicateChangeScriptException { 24 | long lastId = -1; 25 | 26 | for (ChangeScript script : scripts) { 27 | if (script.getId() == lastId) { 28 | throw new DuplicateChangeScriptException("There is more than one change script with number " + lastId); 29 | } 30 | 31 | lastId = script.getId(); 32 | } 33 | 34 | } 35 | 36 | public List getOrderedListOfDoChangeScripts() { 37 | return Collections.unmodifiableList(scripts); 38 | } 39 | 40 | public List getAvailableChangeScripts() { 41 | return getOrderedListOfDoChangeScripts(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/scripts/DirectoryScanner.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.scripts; 2 | 3 | import com.dbdeploy.exceptions.UnrecognisedFilenameException; 4 | 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class DirectoryScanner { 11 | 12 | private final FilenameParser filenameParser = new FilenameParser(); 13 | private final String encoding; 14 | 15 | public DirectoryScanner(String encoding) { 16 | this.encoding = encoding; 17 | } 18 | 19 | public List getChangeScriptsForDirectory(File directory) { 20 | try { 21 | System.err.println("Reading change scripts from directory " + directory.getCanonicalPath() + "..."); 22 | } catch (IOException e1) { 23 | // ignore 24 | } 25 | 26 | List scripts = new ArrayList(); 27 | 28 | for (File file : directory.listFiles()) { 29 | if (file.isFile()) { 30 | String filename = file.getName(); 31 | try { 32 | long id = filenameParser.extractIdFromFilename(filename); 33 | scripts.add(new ChangeScript(id, file, encoding)); 34 | } catch (UnrecognisedFilenameException e) { 35 | // ignore 36 | } 37 | } 38 | } 39 | 40 | return scripts; 41 | 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/java/com/dbdeploy/scripts/FilenameParser.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.scripts; 2 | 3 | import com.dbdeploy.exceptions.UnrecognisedFilenameException; 4 | 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | public class FilenameParser { 9 | private final Pattern pattern; 10 | 11 | public FilenameParser() { 12 | pattern = Pattern.compile("(\\d+).*"); 13 | } 14 | 15 | public long extractIdFromFilename(String filename) throws UnrecognisedFilenameException { 16 | Matcher matches = pattern.matcher(filename); 17 | if (!matches.matches() || matches.groupCount() != 1) 18 | throw new UnrecognisedFilenameException("Could not extract a change script number from filename: " + filename); 19 | 20 | return Long.parseLong(matches.group(1)); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/resources/db2_apply.ftl: -------------------------------------------------------------------------------- 1 | [#ftl] 2 | [#-- @ftlvariable name="changeLogTableName" type="java.lang.String" --] 3 | [#-- @ftlvariable name="delimiter" type="java.lang.String" --] 4 | [#-- @ftlvariable name="separator" type="java.lang.String" --] 5 | [#-- @ftlvariable name="scripts" type="java.util.List" --] 6 | [#list scripts as script] 7 | 8 | -- START CHANGE SCRIPT ${script} 9 | 10 | ${script.content} 11 | 12 | INSERT INTO ${changeLogTableName} (change_number, complete_dt, applied_by, description) 13 | VALUES (${script.id?c}, CURRENT TIMESTAMP, USER, '${script.description}')${separator}${delimiter} 14 | 15 | COMMIT${separator}${delimiter} 16 | 17 | -- END CHANGE SCRIPT ${script} 18 | 19 | [/#list] 20 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/resources/db2_undo.ftl: -------------------------------------------------------------------------------- 1 | [#ftl] 2 | [#-- @ftlvariable name="changeLogTableName" type="java.lang.String" --] 3 | [#-- @ftlvariable name="delimiter" type="java.lang.String" --] 4 | [#-- @ftlvariable name="separator" type="java.lang.String" --] 5 | [#-- @ftlvariable name="scripts" type="java.util.List" --] 6 | [#list scripts as script] 7 | 8 | -- START UNDO OF CHANGE SCRIPT ${script} 9 | 10 | ${script.undoContent} 11 | 12 | DELETE FROM ${changeLogTableName} WHERE change_number = ${script.id?c}${separator}${delimiter} 13 | 14 | COMMIT${separator}${delimiter} 15 | 16 | -- END UNDO OF CHANGE SCRIPT ${script} 17 | 18 | [/#list] 19 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/resources/hsql_apply.ftl: -------------------------------------------------------------------------------- 1 | [#ftl] 2 | [#-- @ftlvariable name="changeLogTableName" type="java.lang.String" --] 3 | [#-- @ftlvariable name="delimiter" type="java.lang.String" --] 4 | [#-- @ftlvariable name="separator" type="java.lang.String" --] 5 | [#-- @ftlvariable name="scripts" type="java.util.List" --] 6 | [#list scripts as script] 7 | 8 | -- START CHANGE SCRIPT ${script} 9 | 10 | ${script.content} 11 | 12 | INSERT INTO ${changeLogTableName} (change_number, complete_dt, applied_by, description) 13 | VALUES (${script.id?c}, CURRENT_TIMESTAMP, USER(), '${script.description}')${separator}${delimiter} 14 | 15 | COMMIT${separator}${delimiter} 16 | 17 | -- END CHANGE SCRIPT ${script} 18 | 19 | [/#list] 20 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/resources/hsql_undo.ftl: -------------------------------------------------------------------------------- 1 | [#ftl] 2 | [#-- @ftlvariable name="changeLogTableName" type="java.lang.String" --] 3 | [#-- @ftlvariable name="delimiter" type="java.lang.String" --] 4 | [#-- @ftlvariable name="separator" type="java.lang.String" --] 5 | [#-- @ftlvariable name="scripts" type="java.util.List" --] 6 | [#list scripts as script] 7 | 8 | -- START UNDO OF CHANGE SCRIPT ${script} 9 | 10 | ${script.undoContent} 11 | 12 | DELETE FROM ${changeLogTableName} WHERE change_number = ${script.id?c}${separator}${delimiter} 13 | 14 | COMMIT${separator}${delimiter} 15 | 16 | -- END UNDO OF CHANGE SCRIPT ${script} 17 | 18 | [/#list] 19 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/resources/mssql_apply.ftl: -------------------------------------------------------------------------------- 1 | [#ftl] 2 | [#-- @ftlvariable name="changeLogTableName" type="java.lang.String" --] 3 | [#-- @ftlvariable name="scripts" type="java.util.List" --] 4 | [#list scripts as script] 5 | 6 | -- START CHANGE SCRIPT ${script} 7 | 8 | ${script.content} 9 | 10 | INSERT INTO ${changeLogTableName} (change_number, complete_dt, applied_by, description) 11 | VALUES (${script.id?c}, getdate(), user_name(), '${script.description}') 12 | GO 13 | 14 | COMMIT 15 | GO 16 | 17 | -- END CHANGE SCRIPT ${script} 18 | 19 | [/#list] -------------------------------------------------------------------------------- /dbdeploy-core/src/main/resources/mssql_undo.ftl: -------------------------------------------------------------------------------- 1 | [#ftl] 2 | [#-- @ftlvariable name="changeLogTableName" type="java.lang.String" --] 3 | [#-- @ftlvariable name="scripts" type="java.util.List" --] 4 | [#list scripts as script] 5 | 6 | -- START UNDO OF CHANGE SCRIPT ${script} 7 | 8 | ${script.undoContent} 9 | 10 | DELETE FROM ${changeLogTableName} WHERE change_number = ${script.id?c} 11 | GO 12 | 13 | COMMIT 14 | GO 15 | 16 | -- END UNDO OF CHANGE SCRIPT ${script} 17 | 18 | [/#list] -------------------------------------------------------------------------------- /dbdeploy-core/src/main/resources/mysql_apply.ftl: -------------------------------------------------------------------------------- 1 | [#ftl] 2 | [#-- @ftlvariable name="changeLogTableName" type="java.lang.String" --] 3 | [#-- @ftlvariable name="delimiter" type="java.lang.String" --] 4 | [#-- @ftlvariable name="separator" type="java.lang.String" --] 5 | [#-- @ftlvariable name="scripts" type="java.util.List" --] 6 | [#list scripts as script] 7 | 8 | -- START CHANGE SCRIPT ${script} 9 | 10 | ${script.content} 11 | 12 | INSERT INTO ${changeLogTableName} (change_number, complete_dt, applied_by, description) 13 | VALUES (${script.id?c}, CURRENT_TIMESTAMP, USER(), '${script.description}')${separator}${delimiter} 14 | 15 | COMMIT${separator}${delimiter} 16 | 17 | -- END CHANGE SCRIPT ${script} 18 | 19 | [/#list] 20 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/resources/mysql_undo.ftl: -------------------------------------------------------------------------------- 1 | [#ftl] 2 | [#-- @ftlvariable name="changeLogTableName" type="java.lang.String" --] 3 | [#-- @ftlvariable name="delimiter" type="java.lang.String" --] 4 | [#-- @ftlvariable name="separator" type="java.lang.String" --] 5 | [#-- @ftlvariable name="scripts" type="java.util.List" --] 6 | [#list scripts as script] 7 | 8 | -- START UNDO OF CHANGE SCRIPT ${script} 9 | 10 | START TRANSACTION${separator}${delimiter} 11 | 12 | ${script.undoContent} 13 | 14 | DELETE FROM ${changeLogTableName} WHERE change_number = ${script.id?c}${separator}${delimiter} 15 | 16 | COMMIT${separator}${delimiter} 17 | 18 | -- END UNDO OF CHANGE SCRIPT ${script} 19 | 20 | [/#list] 21 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/resources/ora_apply.ftl: -------------------------------------------------------------------------------- 1 | [#ftl] 2 | [#-- @ftlvariable name="changeLogTableName" type="java.lang.String" --] 3 | [#-- @ftlvariable name="delimiter" type="java.lang.String" --] 4 | [#-- @ftlvariable name="separator" type="java.lang.String" --] 5 | [#-- @ftlvariable name="scripts" type="java.util.List" --] 6 | [#list scripts as script] 7 | 8 | -- START CHANGE SCRIPT ${script} 9 | 10 | ${script.content} 11 | 12 | INSERT INTO ${changeLogTableName} (change_number, complete_dt, applied_by, description) 13 | VALUES (${script.id?c}, CURRENT_TIMESTAMP, USER, '${script.description}')${separator}${delimiter} 14 | 15 | COMMIT${separator}${delimiter} 16 | 17 | -- END CHANGE SCRIPT ${script} 18 | 19 | [/#list] 20 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/resources/ora_undo.ftl: -------------------------------------------------------------------------------- 1 | [#ftl] 2 | [#-- @ftlvariable name="changeLogTableName" type="java.lang.String" --] 3 | [#-- @ftlvariable name="delimiter" type="java.lang.String" --] 4 | [#-- @ftlvariable name="separator" type="java.lang.String" --] 5 | [#-- @ftlvariable name="scripts" type="java.util.List" --] 6 | [#list scripts as script] 7 | 8 | -- START UNDO OF CHANGE SCRIPT ${script} 9 | 10 | ${script.undoContent} 11 | 12 | DELETE FROM ${changeLogTableName} WHERE change_number = ${script.id?c}${separator}${delimiter} 13 | 14 | COMMIT${separator}${delimiter} 15 | 16 | -- END UNDO OF CHANGE SCRIPT ${script} 17 | 18 | [/#list] 19 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/resources/pgsql_apply.ftl: -------------------------------------------------------------------------------- 1 | [#ftl] 2 | [#-- @ftlvariable name="changeLogTableName" type="java.lang.String" --] 3 | [#-- @ftlvariable name="delimiter" type="java.lang.String" --] 4 | [#-- @ftlvariable name="separator" type="java.lang.String" --] 5 | [#-- @ftlvariable name="scripts" type="java.util.List" --] 6 | [#list scripts as script] 7 | 8 | -- START CHANGE SCRIPT ${script} 9 | 10 | ${script.content} 11 | 12 | INSERT INTO ${changeLogTableName} (change_number, complete_dt, applied_by, description) 13 | VALUES (${script.id?c}, current_timestamp, current_user, '${script.description}')${separator}${delimiter} 14 | 15 | COMMIT${separator}${delimiter} 16 | 17 | -- END CHANGE SCRIPT ${script} 18 | 19 | [/#list] 20 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/resources/pgsql_undo.ftl: -------------------------------------------------------------------------------- 1 | [#ftl] 2 | [#-- @ftlvariable name="changeLogTableName" type="java.lang.String" --] 3 | [#-- @ftlvariable name="delimiter" type="java.lang.String" --] 4 | [#-- @ftlvariable name="separator" type="java.lang.String" --] 5 | [#-- @ftlvariable name="scripts" type="java.util.List" --] 6 | [#list scripts as script] 7 | 8 | -- START UNDO OF CHANGE SCRIPT ${script} 9 | 10 | ${script.undoContent} 11 | 12 | DELETE FROM ${changeLogTableName} WHERE change_number = ${script.id?c}${separator}${delimiter} 13 | 14 | COMMIT${separator}${delimiter} 15 | 16 | -- END UNDO OF CHANGE SCRIPT ${script} 17 | 18 | [/#list] 19 | -------------------------------------------------------------------------------- /dbdeploy-core/src/main/resources/syb-ase_apply.ftl: -------------------------------------------------------------------------------- 1 | [#ftl] 2 | [#-- @ftlvariable name="changeLogTableName" type="java.lang.String" --] 3 | [#-- @ftlvariable name="scripts" type="java.util.List" --] 4 | [#list scripts as script] 5 | 6 | -- START CHANGE SCRIPT ${script} 7 | 8 | ${script.content} 9 | 10 | INSERT INTO ${changeLogTableName} (change_number, complete_dt, applied_by, description) 11 | VALUES (${script.id?c}, getdate(), user_name(), '${script.description}') 12 | GO 13 | 14 | COMMIT 15 | GO 16 | 17 | -- END CHANGE SCRIPT ${script} 18 | 19 | [/#list] -------------------------------------------------------------------------------- /dbdeploy-core/src/main/resources/syb-ase_undo.ftl: -------------------------------------------------------------------------------- 1 | [#ftl] 2 | [#-- @ftlvariable name="changeLogTableName" type="java.lang.String" --] 3 | [#-- @ftlvariable name="scripts" type="java.util.List" --] 4 | [#list scripts as script] 5 | 6 | -- START UNDO OF CHANGE SCRIPT ${script} 7 | 8 | ${script.undoContent} 9 | 10 | DELETE FROM ${changeLogTableName} WHERE change_number = ${script.id?c} 11 | GO 12 | 13 | COMMIT 14 | GO 15 | 16 | -- END UNDO OF CHANGE SCRIPT ${script} 17 | 18 | [/#list] -------------------------------------------------------------------------------- /dbdeploy-core/src/test/java/com/dbdeploy/ControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy; 2 | 3 | import com.dbdeploy.scripts.ChangeScript; 4 | import static org.hamcrest.Matchers.is; 5 | import static org.junit.Assert.assertThat; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.mockito.Mock; 10 | import static org.mockito.Mockito.when; 11 | import org.mockito.runners.MockitoJUnit44Runner; 12 | 13 | import java.util.ArrayList; 14 | import java.util.Arrays; 15 | import java.util.Collections; 16 | import java.util.List; 17 | 18 | @RunWith(MockitoJUnit44Runner.class) 19 | public class ControllerTest { 20 | 21 | @Mock private AvailableChangeScriptsProvider availableChangeScriptsProvider; 22 | @Mock private AppliedChangesProvider appliedChangesProvider; 23 | private Controller controller; 24 | private ChangeScript change1; 25 | private ChangeScript change2; 26 | private ChangeScript change3; 27 | 28 | private StubChangeScriptApplier applier = new StubChangeScriptApplier(); 29 | private StubChangeScriptApplier undoApplier = new StubChangeScriptApplier(); 30 | 31 | @Before 32 | public void setUp() { 33 | controller = new Controller(availableChangeScriptsProvider, appliedChangesProvider, applier, undoApplier); 34 | 35 | change1 = new ChangeScript(1); 36 | change2 = new ChangeScript(2); 37 | change3 = new ChangeScript(3); 38 | 39 | when(availableChangeScriptsProvider.getAvailableChangeScripts()) 40 | .thenReturn(Arrays.asList(change1, change2, change3)); 41 | } 42 | 43 | @Test 44 | public void shouldApplyChangeScriptsInOrder() throws Exception { 45 | when(appliedChangesProvider.getAppliedChanges()).thenReturn(Collections.emptyList()); 46 | 47 | controller.processChangeScripts(Long.MAX_VALUE); 48 | 49 | assertThat(applier.changeScripts.size(), is(3)); 50 | assertThat(applier.changeScripts.get(0), is(change1)); 51 | assertThat(applier.changeScripts.get(1), is(change2)); 52 | assertThat(applier.changeScripts.get(2), is(change3)); 53 | } 54 | 55 | @Test 56 | public void shouldNotCrashWhenPassedANullUndoApplier() throws Exception { 57 | controller = new Controller(availableChangeScriptsProvider, appliedChangesProvider, applier, null); 58 | 59 | when(appliedChangesProvider.getAppliedChanges()).thenReturn(Collections.emptyList()); 60 | 61 | controller.processChangeScripts(Long.MAX_VALUE); 62 | } 63 | 64 | @Test 65 | public void shouldApplyUndoScriptsInReverseOrder() throws Exception { 66 | when(appliedChangesProvider.getAppliedChanges()).thenReturn(Collections.emptyList()); 67 | 68 | controller.processChangeScripts(Long.MAX_VALUE); 69 | 70 | assertThat(undoApplier.changeScripts.size(), is(3)); 71 | assertThat(undoApplier.changeScripts.get(0), is(change3)); 72 | assertThat(undoApplier.changeScripts.get(1), is(change2)); 73 | assertThat(undoApplier.changeScripts.get(2), is(change1)); 74 | } 75 | 76 | 77 | @Test 78 | public void shouldIgnoreChangesAlreadyAppliedToTheDatabase() throws Exception { 79 | when(appliedChangesProvider.getAppliedChanges()).thenReturn(Arrays.asList(1L)); 80 | 81 | controller.processChangeScripts(Long.MAX_VALUE); 82 | 83 | assertThat(applier.changeScripts.size(), is(2)); 84 | assertThat(applier.changeScripts.get(0), is(change2)); 85 | assertThat(applier.changeScripts.get(1), is(change3)); 86 | } 87 | 88 | @Test 89 | public void shouldNotApplyChangesGreaterThanTheMaxChangeToApply() throws Exception { 90 | when(appliedChangesProvider.getAppliedChanges()).thenReturn(Collections.emptyList()); 91 | 92 | controller.processChangeScripts(2L); 93 | 94 | assertThat(applier.changeScripts.size(), is(2)); 95 | assertThat(applier.changeScripts.get(0), is(change1)); 96 | assertThat(applier.changeScripts.get(1), is(change2)); 97 | } 98 | 99 | 100 | 101 | private class StubChangeScriptApplier implements ChangeScriptApplier { 102 | private List changeScripts; 103 | 104 | public void apply(List changeScripts) { 105 | this.changeScripts = new ArrayList(changeScripts); 106 | } 107 | 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/java/com/dbdeploy/DbDeployTest.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy; 2 | 3 | import com.dbdeploy.exceptions.UsageException; 4 | import static org.hamcrest.Matchers.startsWith; 5 | import static org.junit.Assert.*; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | import java.io.File; 10 | 11 | public class DbDeployTest { 12 | private final DbDeploy dbDeploy = new DbDeploy(); 13 | 14 | @Before 15 | public void setSensibleDefaultValuesForAllParameters() { 16 | dbDeploy.setDriver(getClass().getName()); 17 | 18 | dbDeploy.setUserid("someUser"); 19 | 20 | dbDeploy.setDbms("hsql"); 21 | dbDeploy.setUrl("jdbc:hsqldb:mem:dbdeploy"); 22 | 23 | dbDeploy.setScriptdirectory(new File(".")); 24 | dbDeploy.setOutputfile(new File("a.txt")); 25 | } 26 | 27 | @Test(expected = ClassNotFoundException.class) 28 | public void shouldThrowIfInvalidDriverClassNameSpecified() throws Exception { 29 | dbDeploy.setDriver("some.class.that.will.not.be.Found"); 30 | dbDeploy.go(); 31 | } 32 | 33 | @Test(expected = UsageException.class) 34 | public void shouldThrowIfUserIdNotSpecified() throws Exception { 35 | dbDeploy.setUserid(null); 36 | dbDeploy.go(); 37 | } 38 | 39 | @Test(expected = UsageException.class) 40 | public void shouldThrowIfDriverNotSpecified() throws Exception { 41 | dbDeploy.setDriver(null); 42 | dbDeploy.go(); 43 | } 44 | 45 | @Test(expected = UsageException.class) 46 | public void shouldThrowIfUrlNotSpecified() throws Exception { 47 | dbDeploy.setUrl(null); 48 | dbDeploy.go(); 49 | } 50 | 51 | @Test 52 | public void shouldThrowIfScriptDirectoryIsNotAValidDirectory() throws Exception { 53 | dbDeploy.setScriptdirectory(new File("fileThatDoesntExist.txt")); 54 | try { 55 | dbDeploy.go(); 56 | fail("exception expected"); 57 | } catch (UsageException e) { 58 | assertEquals("Script directory must point to a valid directory", e.getMessage()); 59 | } 60 | } 61 | 62 | @Test 63 | public void shouldReportVersionNumberWithoutCrashing() { 64 | assertThat(dbDeploy.getWelcomeString(), startsWith("dbdeploy")); 65 | } 66 | 67 | 68 | } 69 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/java/com/dbdeploy/PrettyPrinterTest.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy; 2 | 3 | import com.dbdeploy.scripts.ChangeScript; 4 | import static org.junit.Assert.*; 5 | import org.junit.Test; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | 10 | import com.dbdeploy.PrettyPrinter; 11 | 12 | public class PrettyPrinterTest { 13 | PrettyPrinter prettyPrinter = new PrettyPrinter(); 14 | 15 | @Test 16 | public void shouldDisplayNonRangedNumbersAsSeperateEntities() throws Exception { 17 | assertEquals("1, 3, 5", prettyPrinter.format(Arrays.asList(1L, 3L, 5L))); 18 | } 19 | 20 | @Test 21 | public void shouldDisplayARangeAsSuch() throws Exception { 22 | assertEquals("1..5", prettyPrinter.format(Arrays.asList(1L, 2L, 3L, 4L, 5L))); 23 | } 24 | 25 | @Test 26 | public void rangesOfTwoAreNotDisplayedAsARange() throws Exception { 27 | assertEquals("1, 2", prettyPrinter.format(Arrays.asList(1L, 2L))); 28 | } 29 | 30 | @Test 31 | public void shouldReturnNoneWithAnEmptyList() throws Exception { 32 | assertEquals("(none)", prettyPrinter.format(new ArrayList())); 33 | } 34 | 35 | @Test 36 | public void canDealWithMixtureOfRangesAndNonRanges() throws Exception { 37 | assertEquals("1, 2, 4, 7..10, 12", prettyPrinter.format(Arrays.asList(1L, 2L, 4L, 7L, 8L, 9L, 10L, 12L))); 38 | } 39 | 40 | @Test 41 | public void canFormatAChangeScriptList() throws Exception { 42 | ChangeScript change1 = new ChangeScript(1); 43 | ChangeScript change3 = new ChangeScript(3); 44 | assertEquals("1, 3", prettyPrinter.formatChangeScriptList(Arrays.asList(change1, change3))); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/java/com/dbdeploy/appliers/DirectToDbApplierTest.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.appliers; 2 | 3 | import com.dbdeploy.database.QueryStatementSplitter; 4 | import com.dbdeploy.database.changelog.DatabaseSchemaVersionManager; 5 | import com.dbdeploy.database.changelog.QueryExecuter; 6 | import com.dbdeploy.exceptions.ChangeScriptFailedException; 7 | import com.dbdeploy.scripts.ChangeScript; 8 | import com.dbdeploy.scripts.StubChangeScript; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | import org.mockito.Mock; 13 | import org.mockito.runners.MockitoJUnit44Runner; 14 | 15 | import java.sql.SQLException; 16 | import java.util.Arrays; 17 | 18 | import static org.hamcrest.Matchers.*; 19 | import static org.junit.Assert.assertThat; 20 | import static org.junit.Assert.fail; 21 | import static org.mockito.Mockito.*; 22 | 23 | @RunWith(MockitoJUnit44Runner.class) 24 | public class DirectToDbApplierTest { 25 | @Mock private QueryExecuter queryExecuter; 26 | @Mock private DatabaseSchemaVersionManager schemaVersionManager; 27 | @Mock private QueryStatementSplitter splitter; 28 | private DirectToDbApplier applier; 29 | 30 | @Before 31 | public void setUp() { 32 | applier = new DirectToDbApplier(queryExecuter, schemaVersionManager, splitter); 33 | } 34 | 35 | @Test 36 | public void shouldSetConnectionToManualCommitModeAtStart() throws Exception { 37 | applier.begin(); 38 | 39 | verify(queryExecuter).setAutoCommit(false); 40 | } 41 | 42 | @Test 43 | public void shouldApplyChangeScriptBySplittingContentUsingTheSplitter() throws Exception { 44 | when(splitter.split("split; content")).thenReturn(Arrays.asList("split", "content")); 45 | 46 | applier.applyChangeScript(new StubChangeScript(1, "script", "split; content")); 47 | 48 | verify(queryExecuter).execute("split"); 49 | verify(queryExecuter).execute("content"); 50 | } 51 | 52 | @Test 53 | public void shouldRethrowSqlExceptionsWithInformationAboutWhatStringFailed() throws Exception { 54 | when(splitter.split("split; content")).thenReturn(Arrays.asList("split", "content")); 55 | ChangeScript script = new StubChangeScript(1, "script", "split; content"); 56 | 57 | doThrow(new SQLException("dummy exception")).when(queryExecuter).execute("split"); 58 | 59 | try { 60 | applier.applyChangeScript(script); 61 | fail("exception expected"); 62 | } catch (ChangeScriptFailedException e) { 63 | assertThat(e.getExecutedSql(), is("split")); 64 | assertThat(e.getScript(), is(script)); 65 | } 66 | 67 | verify(queryExecuter, never()).execute("content"); 68 | } 69 | 70 | @Test 71 | public void shouldInsertToSchemaVersionTable() throws Exception { 72 | ChangeScript changeScript = new ChangeScript(1, "script.sql"); 73 | 74 | applier.insertToSchemaVersionTable(changeScript); 75 | 76 | verify(schemaVersionManager).recordScriptApplied(changeScript); 77 | 78 | } 79 | 80 | @Test 81 | public void shouldCommitTransactionOnErrrCommitTransaction() throws Exception { 82 | applier.commitTransaction(); 83 | 84 | verify(queryExecuter).commit(); 85 | } 86 | 87 | 88 | } 89 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/java/com/dbdeploy/appliers/TemplateBasedApplierTest.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.appliers; 2 | 3 | import com.dbdeploy.database.DelimiterType; 4 | import com.dbdeploy.exceptions.UsageException; 5 | import org.apache.commons.io.output.NullWriter; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | import static org.hamcrest.Matchers.is; 10 | import static org.junit.Assert.assertThat; 11 | 12 | public class TemplateBasedApplierTest { 13 | 14 | @Test 15 | public void shouldThrowUsageExceptionWhenTemplateNotFound() throws Exception { 16 | TemplateBasedApplier applier = new TemplateBasedApplier(new NullWriter(), "some_complete_rubbish", null, ";", DelimiterType.normal, null); 17 | try { 18 | applier.apply(null); 19 | Assert.fail("expected exception"); 20 | } catch (UsageException e) { 21 | assertThat(e.getMessage(), is("Could not find template named some_complete_rubbish_apply.ftl\n" + 22 | "Check that you have got the name of the database syntax correct.")); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/java/com/dbdeploy/database/QueryStatementSplitterTest.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.database; 2 | 3 | import org.apache.commons.lang.SystemUtils; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import java.util.List; 8 | 9 | import static org.hamcrest.Matchers.*; 10 | import static org.junit.Assert.assertThat; 11 | 12 | public class QueryStatementSplitterTest { 13 | private QueryStatementSplitter splitter; 14 | 15 | @Before 16 | public void setUp() throws Exception { 17 | splitter = new QueryStatementSplitter(); 18 | } 19 | 20 | 21 | @Test 22 | public void shouldNotSplitStatementsThatHaveNoDelimter() throws Exception { 23 | List result = splitter.split("SELECT 1"); 24 | assertThat(result, hasItem("SELECT 1")); 25 | assertThat(result.size(), is(1)); 26 | } 27 | 28 | @Test 29 | public void shouldIgnoreSemicolonsInTheMiddleOfALine() throws Exception { 30 | List result = splitter.split("SELECT ';'"); 31 | assertThat(result, hasItem("SELECT ';'")); 32 | assertThat(result.size(), is(1)); 33 | } 34 | 35 | @Test 36 | public void shouldSplitStatementsOnASemicolonAtTheEndOfALine() throws Exception { 37 | List result = splitter.split("SELECT 1;\nSELECT 2;"); 38 | assertThat(result, hasItems("SELECT 1", "SELECT 2")); 39 | assertThat(result.size(), is(2)); 40 | } 41 | 42 | @Test 43 | public void shouldSplitStatementsOnASemicolonAtTheEndOfALineEvenWithWindowsLineEndings() throws Exception { 44 | List result = splitter.split("SELECT 1;\r\nSELECT 2;"); 45 | assertThat(result, hasItems("SELECT 1", "SELECT 2")); 46 | assertThat(result.size(), is(2)); 47 | } 48 | 49 | @Test 50 | public void shouldSplitStatementsOnASemicolonAtTheEndOfALineIgnoringWhitespace() throws Exception { 51 | List result = splitter.split("SELECT 1; \nSELECT 2; "); 52 | assertThat(result, hasItems("SELECT 1", "SELECT 2")); 53 | assertThat(result.size(), is(2)); 54 | } 55 | 56 | @Test 57 | public void shouldLeaveLineBreaksAlone() throws Exception { 58 | assertThat(splitter.split("SELECT\n1"), hasItems("SELECT" + SystemUtils.LINE_SEPARATOR + "1")); 59 | assertThat(splitter.split("SELECT\r\n1"), hasItems("SELECT" + SystemUtils.LINE_SEPARATOR + "1")); 60 | } 61 | 62 | @Test 63 | public void shouldSupportRowStyleTerminators() throws Exception { 64 | splitter.setDelimiter("/"); 65 | splitter.setDelimiterType(DelimiterType.row); 66 | 67 | List result = splitter.split("SHOULD IGNORE /\nAT THE END OF A LINE\n/\nSELECT BLAH FROM DUAL"); 68 | assertThat(result, hasItems("SHOULD IGNORE /" + SystemUtils.LINE_SEPARATOR + "AT THE END OF A LINE" + SystemUtils.LINE_SEPARATOR, "SELECT BLAH FROM DUAL")); 69 | assertThat(result.size(), is(2)); 70 | } 71 | 72 | @Test 73 | public void shouldSupportDefinedNewLineCharacters() throws Exception { 74 | splitter.setOutputLineEnding(LineEnding.crlf); 75 | assertThat(splitter.split("SELECT\n1"), hasItems("SELECT\r\n1")); 76 | assertThat(splitter.split("SELECT\r\n1"), hasItems("SELECT\r\n1")); 77 | 78 | splitter.setOutputLineEnding(LineEnding.cr); 79 | assertThat(splitter.split("SELECT\n1"), hasItems("SELECT\r1")); 80 | assertThat(splitter.split("SELECT\r\n1"), hasItems("SELECT\r1")); 81 | 82 | 83 | splitter.setOutputLineEnding(LineEnding.lf); 84 | assertThat(splitter.split("SELECT\n1"), hasItems("SELECT\n1")); 85 | assertThat(splitter.split("SELECT\r\n1"), hasItems("SELECT\n1")); 86 | 87 | 88 | splitter.setOutputLineEnding(LineEnding.platform); 89 | assertThat(splitter.split("SELECT\n1"), hasItems("SELECT" + SystemUtils.LINE_SEPARATOR + "1")); 90 | assertThat(splitter.split("SELECT\r\n1"), hasItems("SELECT" + SystemUtils.LINE_SEPARATOR + "1")); 91 | } 92 | 93 | 94 | } 95 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/java/com/dbdeploy/database/ScriptGenerationTest.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.database; 2 | 3 | import com.dbdeploy.ChangeScriptApplier; 4 | import com.dbdeploy.Controller; 5 | import com.dbdeploy.appliers.TemplateBasedApplier; 6 | import com.dbdeploy.database.changelog.DatabaseSchemaVersionManager; 7 | import com.dbdeploy.exceptions.SchemaVersionTrackingException; 8 | import com.dbdeploy.scripts.ChangeScript; 9 | import com.dbdeploy.scripts.ChangeScriptRepository; 10 | import com.dbdeploy.scripts.StubChangeScript; 11 | import org.junit.Test; 12 | 13 | import java.io.*; 14 | import java.util.Arrays; 15 | import java.util.Collections; 16 | import java.util.List; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | 20 | public class ScriptGenerationTest { 21 | 22 | @Test 23 | public void generateConsolidatedChangesScriptForAllDatabasesAndCompareAgainstTemplate() throws Exception { 24 | for (String syntax : Arrays.asList("hsql", "mssql", "mysql", "ora", "syb-ase", "db2", "pgsql")) { 25 | try { 26 | System.out.printf("Testing syntax %s\n", syntax); 27 | runIntegratedTestAndConfirmOutputResults(syntax); 28 | } catch (Exception e) { 29 | throw new RuntimeException("Failed while testing syntax " + syntax, e); 30 | } 31 | } 32 | } 33 | 34 | private void runIntegratedTestAndConfirmOutputResults(String syntaxName) throws Exception { 35 | 36 | StringWriter writer = new StringWriter(); 37 | 38 | ChangeScript changeOne = new StubChangeScript(1, "001_change.sql", "-- contents of change script 1"); 39 | ChangeScript changeTwo = new StubChangeScript(2, "002_change.sql", "-- contents of change script 2"); 40 | 41 | List changeScripts = Arrays.asList(changeOne, changeTwo); 42 | ChangeScriptRepository changeScriptRepository = new ChangeScriptRepository(changeScripts); 43 | 44 | 45 | 46 | final StubSchemaManager schemaManager = new StubSchemaManager(); 47 | ChangeScriptApplier applier = new TemplateBasedApplier(writer, syntaxName, "changelog", ";", DelimiterType.normal, null); 48 | Controller controller = new Controller(changeScriptRepository, schemaManager, applier, null); 49 | 50 | controller.processChangeScripts(Long.MAX_VALUE); 51 | 52 | assertEquals(readExpectedFileContents(getExpectedFilename(syntaxName)), writer.toString()); 53 | } 54 | 55 | private String getExpectedFilename(String dbSyntaxName) { 56 | return dbSyntaxName + "_expected.sql"; 57 | } 58 | 59 | private String readExpectedFileContents(String expectedFilename) throws IOException { 60 | final InputStream stream = getClass().getResourceAsStream(expectedFilename); 61 | BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); 62 | try { 63 | return readEntireStreamIntoAStringWithConversionToSystemDependantLineTerminators(reader); 64 | } finally { 65 | reader.close(); 66 | } 67 | } 68 | 69 | private String readEntireStreamIntoAStringWithConversionToSystemDependantLineTerminators(BufferedReader reader) throws IOException { 70 | StringWriter contentWithSystemDependentLineTerminators = new StringWriter(); 71 | PrintWriter newLineConvertingContentWriter = new PrintWriter(contentWithSystemDependentLineTerminators); 72 | try { 73 | String line; 74 | while ((line = reader.readLine()) != null) { 75 | newLineConvertingContentWriter.println(line); 76 | } 77 | newLineConvertingContentWriter.flush(); 78 | return contentWithSystemDependentLineTerminators.toString(); 79 | } finally { 80 | newLineConvertingContentWriter.close(); 81 | } 82 | } 83 | 84 | 85 | private class StubSchemaManager extends DatabaseSchemaVersionManager { 86 | public StubSchemaManager() { 87 | super(null, "changelog"); 88 | } 89 | 90 | @Override 91 | public List getAppliedChanges() throws SchemaVersionTrackingException { 92 | return Collections.emptyList(); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/java/com/dbdeploy/database/changelog/DatabaseSchemaVersionManagerTest.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.database.changelog; 2 | 3 | import com.dbdeploy.scripts.ChangeScript; 4 | import org.hamcrest.Matchers; 5 | import static org.hamcrest.Matchers.equalToIgnoringWhiteSpace; 6 | import static org.hamcrest.Matchers.hasItems; 7 | import static org.junit.Assert.assertThat; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | import static org.mockito.Matchers.anyString; 11 | import static org.mockito.Matchers.startsWith; 12 | import org.mockito.Mock; 13 | import static org.mockito.Mockito.verify; 14 | import static org.mockito.Mockito.when; 15 | import org.mockito.MockitoAnnotations; 16 | 17 | import java.sql.ResultSet; 18 | import java.sql.SQLException; 19 | import java.sql.Timestamp; 20 | import java.util.Date; 21 | import java.util.List; 22 | 23 | public class DatabaseSchemaVersionManagerTest { 24 | private final ChangeScript script = new ChangeScript(99, "Some Description"); 25 | 26 | private DatabaseSchemaVersionManager schemaVersionManager; 27 | 28 | @Mock private ResultSet expectedResultSet; 29 | @Mock private QueryExecuter queryExecuter; 30 | @Mock private DatabaseSchemaVersionManager.CurrentTimeProvider timeProvider; 31 | 32 | @Before 33 | public void setUp() throws SQLException { 34 | MockitoAnnotations.initMocks(this); 35 | 36 | when(queryExecuter.executeQuery(anyString())).thenReturn(expectedResultSet); 37 | 38 | schemaVersionManager = new DatabaseSchemaVersionManager(queryExecuter, "changelog"); 39 | schemaVersionManager.setTimeProvider(timeProvider); 40 | } 41 | 42 | @Test 43 | public void shouldUseQueryExecuterToReadInformationFromTheChangelogTable() throws Exception { 44 | when(expectedResultSet.next()).thenReturn(true, true, true, false); 45 | when(expectedResultSet.getLong(1)).thenReturn(5L, 9L, 12L); 46 | 47 | final List numbers = schemaVersionManager.getAppliedChanges(); 48 | assertThat(numbers, hasItems(5L, 9L, 12L)); 49 | } 50 | 51 | 52 | @Test 53 | public void shouldUpdateChangelogTable() throws Exception { 54 | Date now = new Date(); 55 | 56 | when(queryExecuter.getDatabaseUsername()).thenReturn("DBUSER"); 57 | when(timeProvider.now()).thenReturn(now); 58 | 59 | schemaVersionManager.recordScriptApplied(script); 60 | String expected = 61 | "INSERT INTO changelog (change_number, complete_dt, applied_by, description) " + 62 | "VALUES (?, ?, ?, ?)"; 63 | 64 | verify(queryExecuter).execute(expected, script.getId(), 65 | new Timestamp(now.getTime()), "DBUSER", script.getDescription()); 66 | } 67 | 68 | @Test 69 | public void shouldGenerateSqlStringToDeleteChangelogTableAfterUndoScriptApplication() throws Exception { 70 | String sql = schemaVersionManager.getChangelogDeleteSql(script); 71 | String expected = 72 | "DELETE FROM changelog WHERE change_number = 99"; 73 | assertThat(sql, equalToIgnoringWhiteSpace(expected)); 74 | } 75 | 76 | @Test 77 | public void shouldGetAppliedChangesFromSpecifiedChangelogTableName() throws SQLException { 78 | DatabaseSchemaVersionManager schemaVersionManagerWithDifferentTableName = 79 | new DatabaseSchemaVersionManager(queryExecuter, 80 | "user_specified_changelog"); 81 | 82 | schemaVersionManagerWithDifferentTableName.getAppliedChanges(); 83 | 84 | verify(queryExecuter).executeQuery(startsWith("SELECT change_number FROM user_specified_changelog ")); 85 | } 86 | 87 | @Test 88 | public void shouldGenerateSqlStringContainingSpecifiedChangelogTableNameOnDelete() { 89 | DatabaseSchemaVersionManager schemaVersionManagerWithDifferentTableName = 90 | new DatabaseSchemaVersionManager(queryExecuter, 91 | "user_specified_changelog"); 92 | 93 | String updateSql = schemaVersionManagerWithDifferentTableName.getChangelogDeleteSql(script); 94 | 95 | assertThat(updateSql, Matchers.startsWith("DELETE FROM user_specified_changelog ")); 96 | } 97 | } 98 | 99 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/java/com/dbdeploy/integration/Database.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.integration; 2 | 3 | import com.dbdeploy.DbDeploy; 4 | import com.dbdeploy.database.changelog.DatabaseSchemaVersionManager; 5 | import com.dbdeploy.database.changelog.QueryExecuter; 6 | import com.dbdeploy.exceptions.SchemaVersionTrackingException; 7 | import org.apache.commons.io.FileUtils; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | import java.sql.*; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | public class Database { 16 | String connectionString; 17 | Connection connection; 18 | private final String changeLogTableName; 19 | 20 | private static final String DATABASE_SYNTAX = "hsql"; 21 | private static final String DATABASE_DRIVER = "org.hsqldb.jdbcDriver"; 22 | private static final String DATABASE_USERNAME = "sa"; 23 | private static final String DATABASE_PASSWORD = ""; 24 | 25 | public Database(String databaseName) throws ClassNotFoundException, SQLException { 26 | this(databaseName, "changelog"); 27 | } 28 | 29 | public Database(String databaseName, String changeLogTableName) throws ClassNotFoundException, SQLException { 30 | this.changeLogTableName = changeLogTableName; 31 | connectionString = "jdbc:hsqldb:mem:" + databaseName; 32 | connection = openConnection(); 33 | } 34 | 35 | private Connection openConnection() throws ClassNotFoundException, SQLException { 36 | Class.forName(DATABASE_DRIVER); 37 | return DriverManager.getConnection(connectionString, DATABASE_USERNAME, DATABASE_PASSWORD); 38 | } 39 | 40 | public void createSchemaVersionTable() throws SQLException { 41 | execute("CREATE TABLE " + changeLogTableName + 42 | " ( " + 43 | " change_number INTEGER NOT NULL, " + 44 | " complete_dt TIMESTAMP NOT NULL, " + 45 | " applied_by VARCHAR(100) NOT NULL, " + 46 | " description VARCHAR(500) NOT NULL " + 47 | ")"); 48 | 49 | execute("ALTER TABLE " + changeLogTableName + 50 | " ADD CONSTRAINT Pkchangelog PRIMARY KEY (change_number)"); 51 | } 52 | 53 | private void execute(String sql) throws SQLException { 54 | final Statement statement = connection.createStatement(); 55 | statement.execute(sql); 56 | statement.close(); 57 | } 58 | 59 | public void applyDatabaseSettingsTo(DbDeploy dbDeploy) { 60 | dbDeploy.setDbms(DATABASE_SYNTAX); 61 | dbDeploy.setDriver(DATABASE_DRIVER); 62 | dbDeploy.setUrl(connectionString); 63 | dbDeploy.setUserid(DATABASE_USERNAME); 64 | dbDeploy.setPassword(DATABASE_PASSWORD); 65 | } 66 | 67 | public void applyScript(File sqlFile) throws SQLException, IOException { 68 | String sql = FileUtils.readFileToString(sqlFile); 69 | 70 | final String[] statements = sql.split(";"); 71 | 72 | for (String statement : statements) { 73 | execute(statement); 74 | } 75 | } 76 | 77 | public List executeQuery(String sql) throws SQLException { 78 | final Statement statement = connection.createStatement(); 79 | final ResultSet rs = statement.executeQuery(sql); 80 | 81 | 82 | List results = new ArrayList(); 83 | 84 | ResultSetMetaData meta = rs.getMetaData(); 85 | int colmax = meta.getColumnCount(); 86 | 87 | for (; rs.next();) { 88 | Object[] thisRow = new Object[colmax]; 89 | for (int i = 0; i < colmax; ++i) { 90 | thisRow[i] = rs.getObject(i + 1); 91 | } 92 | 93 | results.add(thisRow); 94 | } 95 | 96 | statement.close(); 97 | 98 | return results; 99 | } 100 | 101 | public List getChangelogEntries() throws SchemaVersionTrackingException, SQLException { 102 | final QueryExecuter queryExecuter = new QueryExecuter(connectionString, DATABASE_USERNAME, DATABASE_PASSWORD); 103 | 104 | DatabaseSchemaVersionManager schemaVersionManager = 105 | new DatabaseSchemaVersionManager(queryExecuter, changeLogTableName); 106 | return schemaVersionManager.getAppliedChanges(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/java/com/dbdeploy/integration/DirectToDbIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.integration; 2 | 3 | import com.dbdeploy.DbDeploy; 4 | import org.junit.Test; 5 | 6 | import java.io.File; 7 | import java.util.List; 8 | 9 | import static org.hamcrest.MatcherAssert.assertThat; 10 | import static org.hamcrest.Matchers.*; 11 | import static org.junit.Assert.fail; 12 | 13 | public class DirectToDbIntegrationTest { 14 | @Test 15 | public void shouldSuccessfullyApplyAValidSetOfDeltas() throws Exception { 16 | Database db = new Database("todb_success_test"); 17 | db.createSchemaVersionTable(); 18 | 19 | DbDeploy dbDeploy = new DbDeploy(); 20 | db.applyDatabaseSettingsTo(dbDeploy); 21 | dbDeploy.setScriptdirectory(findScriptDirectory("src/it/db/deltas")); 22 | dbDeploy.go(); 23 | 24 | assertThat(db.getChangelogEntries(), hasItems(1L, 2L)); 25 | 26 | List results = db.executeQuery("select id from Test"); 27 | assertThat(results.size(), is(1)); 28 | assertThat((Integer) results.get(0)[0], is(6)); 29 | } 30 | 31 | @Test 32 | public void shouldSuccessfullyApplyAValidSetOfDeltasIncludingMutliStatementDeltas() throws Exception { 33 | Database db = new Database("todb_multistatement_test"); 34 | db.createSchemaVersionTable(); 35 | 36 | DbDeploy dbDeploy = new DbDeploy(); 37 | db.applyDatabaseSettingsTo(dbDeploy); 38 | dbDeploy.setScriptdirectory(findScriptDirectory("src/it/db/multi_statement_deltas")); 39 | dbDeploy.go(); 40 | 41 | assertThat(db.getChangelogEntries(), hasItems(1L, 2L)); 42 | 43 | List results = db.executeQuery("select id from Test"); 44 | assertThat(results.size(), is(2)); 45 | assertThat(results, hasItems(new Object[] {6}, new Object[] {7})); 46 | } 47 | 48 | 49 | @Test 50 | public void shouldBeAbleToRecoverFromBadScriptsJustByRunningCorrectedScriptsAgain() throws Exception { 51 | Database db = new Database("todb_failure_recovery_test"); 52 | db.createSchemaVersionTable(); 53 | 54 | DbDeploy dbDeploy = new DbDeploy(); 55 | db.applyDatabaseSettingsTo(dbDeploy); 56 | dbDeploy.setScriptdirectory(findScriptDirectory("src/it/db/invalid_deltas")); 57 | try { 58 | dbDeploy.go(); 59 | } catch (Exception ex) { 60 | //expected 61 | assertThat(ex.getMessage(), containsString("Column count does not match in statement")); 62 | } 63 | 64 | // script 2 failed, so it should not be considered applied to the database 65 | assertThat(db.getChangelogEntries(), hasItems(1L)); 66 | assertThat(db.getChangelogEntries(), not(hasItems(2L))); 67 | 68 | List results = db.executeQuery("select id from Test"); 69 | assertThat(results.size(), is(0)); 70 | 71 | // now run dbdeploy again with valid scripts, should recover 72 | dbDeploy.setScriptdirectory(findScriptDirectory("src/it/db/deltas")); 73 | dbDeploy.go(); 74 | 75 | assertThat(db.getChangelogEntries(), hasItems(1L, 2L)); 76 | 77 | results = db.executeQuery("select id from Test"); 78 | assertThat(results.size(), is(1)); 79 | } 80 | 81 | private File findScriptDirectory(String directoryName) { 82 | File directoryWhenRunningUnderMaven = new File(directoryName); 83 | if (directoryWhenRunningUnderMaven.isDirectory()) { 84 | return directoryWhenRunningUnderMaven; 85 | } 86 | 87 | File directoryWhenRunningUnderIde = new File("dbdeploy-core", directoryName); 88 | if (directoryWhenRunningUnderIde.isDirectory()) { 89 | return directoryWhenRunningUnderIde; 90 | } 91 | 92 | fail("Could not find script directory: " + directoryName); 93 | 94 | return null; 95 | } 96 | 97 | } -------------------------------------------------------------------------------- /dbdeploy-core/src/test/java/com/dbdeploy/integration/OutputToFileIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.integration; 2 | 3 | import com.dbdeploy.DbDeploy; 4 | import static org.hamcrest.MatcherAssert.assertThat; 5 | import static org.hamcrest.Matchers.*; 6 | import org.junit.Test; 7 | import static org.junit.Assert.fail; 8 | 9 | import java.io.File; 10 | import java.sql.SQLException; 11 | import java.util.List; 12 | 13 | public class OutputToFileIntegrationTest { 14 | @Test 15 | public void shouldSuccessfullyApplyAValidSetOfDeltas() throws Exception { 16 | Database db = new Database("success_test"); 17 | db.createSchemaVersionTable(); 18 | 19 | File outputFile = File.createTempFile("success",".sql"); 20 | 21 | DbDeploy dbDeploy = new DbDeploy(); 22 | db.applyDatabaseSettingsTo(dbDeploy); 23 | dbDeploy.setScriptdirectory(findScriptDirectory("src/it/db/deltas")); 24 | dbDeploy.setOutputfile(outputFile); 25 | dbDeploy.go(); 26 | 27 | db.applyScript(outputFile); 28 | 29 | assertThat(db.getChangelogEntries(), hasItems(1L, 2L)); 30 | 31 | List results = db.executeQuery("select id from Test"); 32 | assertThat(results.size(), is(1)); 33 | assertThat((Integer) results.get(0)[0], is(6)); 34 | } 35 | 36 | @Test 37 | public void shouldBeAbleToRecoverFromBadScriptsJustByRunningCorrectedScriptsAgain() throws Exception { 38 | File outputFile = File.createTempFile("recovery",".sql"); 39 | 40 | Database db = new Database("failure_recovery_test"); 41 | db.createSchemaVersionTable(); 42 | 43 | DbDeploy dbDeploy = new DbDeploy(); 44 | db.applyDatabaseSettingsTo(dbDeploy); 45 | dbDeploy.setScriptdirectory(findScriptDirectory("src/it/db/invalid_deltas")); 46 | dbDeploy.setOutputfile(outputFile); 47 | dbDeploy.go(); 48 | 49 | try { 50 | db.applyScript(outputFile); 51 | } catch (SQLException ex) { 52 | //expected 53 | assertThat(ex.getMessage(), startsWith("Column count does not match in statement")); 54 | } 55 | 56 | // script 2 failed, so it should not be considered applied to the database 57 | assertThat(db.getChangelogEntries(), hasItems(1L)); 58 | assertThat(db.getChangelogEntries(), not(hasItems(2L))); 59 | 60 | List results = db.executeQuery("select id from Test"); 61 | assertThat(results.size(), is(0)); 62 | 63 | // now run dbdeploy again with valid scripts, should recover 64 | dbDeploy.setScriptdirectory(findScriptDirectory("src/it/db/deltas")); 65 | dbDeploy.setOutputfile(outputFile); 66 | dbDeploy.go(); 67 | 68 | db.applyScript(outputFile); 69 | 70 | assertThat(db.getChangelogEntries(), hasItems(1L, 2L)); 71 | 72 | results = db.executeQuery("select id from Test"); 73 | assertThat(results.size(), is(1)); 74 | } 75 | 76 | @Test 77 | public void shouldUseSpecifiedChangeLogTable() throws Exception { 78 | Database db = new Database("user_defined_changelog_test", "user_defined_changelog_table"); 79 | db.createSchemaVersionTable(); 80 | 81 | File outputFile = File.createTempFile("changelog_success", ".sql"); 82 | 83 | DbDeploy dbDeploy = new DbDeploy(); 84 | db.applyDatabaseSettingsTo(dbDeploy); 85 | dbDeploy.setScriptdirectory(findScriptDirectory("src/it/db/deltas")); 86 | dbDeploy.setOutputfile(outputFile); 87 | dbDeploy.setChangeLogTableName("user_defined_changelog_table"); 88 | dbDeploy.go(); 89 | 90 | db.applyScript(outputFile); 91 | 92 | assertThat(db.getChangelogEntries(), hasItems(1L, 2L)); 93 | } 94 | 95 | @Test 96 | public void shouldNotAddCommaSeparatorsToHighNumberedScripts() throws Exception { 97 | Database db = new Database("high_number_test"); 98 | db.createSchemaVersionTable(); 99 | 100 | File outputFile = File.createTempFile("high_number_test",".sql"); 101 | 102 | DbDeploy dbDeploy = new DbDeploy(); 103 | db.applyDatabaseSettingsTo(dbDeploy); 104 | dbDeploy.setScriptdirectory(findScriptDirectory("src/it/db/high_numbers")); 105 | dbDeploy.setOutputfile(outputFile); 106 | dbDeploy.go(); 107 | 108 | db.applyScript(outputFile); 109 | } 110 | 111 | private File findScriptDirectory(String directoryName) { 112 | File directoryWhenRunningUnderMaven = new File(directoryName); 113 | if (directoryWhenRunningUnderMaven.isDirectory()) { 114 | return directoryWhenRunningUnderMaven; 115 | } 116 | 117 | File directoryWhenRunningUnderIde = new File("dbdeploy-core", directoryName); 118 | if (directoryWhenRunningUnderIde.isDirectory()) { 119 | return directoryWhenRunningUnderIde; 120 | } 121 | 122 | fail("Could not find script directory: " + directoryName); 123 | 124 | return null; 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/java/com/dbdeploy/scripts/ChangeScriptCreatorTest.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.scripts; 2 | 3 | import java.io.File; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import static org.junit.Assert.*; 7 | 8 | /** 9 | * @author jbogan 10 | */ 11 | public class ChangeScriptCreatorTest { 12 | private ChangeScriptCreator changeScriptCreator; 13 | private File scriptDirectory; 14 | 15 | @Before 16 | public void setUp() { 17 | scriptDirectory = new File(System.getProperty("java.io.tmpdir")); 18 | changeScriptCreator = new ChangeScriptCreator(); 19 | changeScriptCreator.setScriptDirectory(scriptDirectory); 20 | } 21 | 22 | @Test 23 | public void defaultChangeScriptCreatorCreatesScriptWithTimestamp() throws Exception { 24 | final File newChangeScript = changeScriptCreator.createScript(); 25 | assertTrue(newChangeScript.exists()); 26 | 27 | final String newChangeScriptFileName = newChangeScript.getName(); 28 | assertTrue("file name doesnt have sql suffix", newChangeScriptFileName.endsWith(".sql")); 29 | assertTrue("file name not timestamped correctly", newChangeScriptFileName.matches("[0-9]{14}\\.sql")); 30 | } 31 | 32 | @Test 33 | public void createsScriptWithTimestampAndDescription() throws Exception { 34 | final String scriptDescription = "test_1234"; 35 | changeScriptCreator.setScriptDescription(scriptDescription); 36 | final File newChangeScript = changeScriptCreator.createScript(); 37 | 38 | final String newChangeScriptFileName = newChangeScript.getName(); 39 | assertTrue("file name not timestamped and named correctly", newChangeScriptFileName.matches("[0-9]{14}_" + scriptDescription + "\\.sql")); 40 | } 41 | } -------------------------------------------------------------------------------- /dbdeploy-core/src/test/java/com/dbdeploy/scripts/ChangeScriptRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.scripts; 2 | 3 | import com.dbdeploy.exceptions.DuplicateChangeScriptException; 4 | import com.dbdeploy.scripts.ChangeScript; 5 | import com.dbdeploy.scripts.ChangeScriptRepository; 6 | import static org.hamcrest.Matchers.*; 7 | import static org.junit.Assert.*; 8 | import org.junit.Test; 9 | 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | public class ChangeScriptRepositoryTest { 14 | 15 | @Test 16 | public void shouldReturnAnOrderedListOfChangeScripts() throws Exception { 17 | ChangeScript one = new ChangeScript(1); 18 | ChangeScript two = new ChangeScript(2); 19 | ChangeScript three = new ChangeScript(3); 20 | ChangeScript four = new ChangeScript(4); 21 | 22 | ChangeScriptRepository repository = new ChangeScriptRepository(Arrays.asList( three, two, four, one )); 23 | 24 | List list = repository.getOrderedListOfDoChangeScripts(); 25 | assertThat(4, equalTo(list.size())); 26 | assertSame(one, list.get(0)); 27 | assertSame(two, list.get(1)); 28 | assertSame(three, list.get(2)); 29 | assertSame(four, list.get(3)); 30 | } 31 | 32 | @Test 33 | public void shouldThrowWhenChangeScriptListContainsDuplicates() throws Exception { 34 | ChangeScript two = new ChangeScript(2); 35 | ChangeScript three = new ChangeScript(3); 36 | ChangeScript anotherTwo = new ChangeScript(2); 37 | 38 | try { 39 | new ChangeScriptRepository(Arrays.asList(three, two, anotherTwo)); 40 | fail("expected exception"); 41 | } catch (DuplicateChangeScriptException ex) { 42 | assertEquals("There is more than one change script with number 2", ex.getMessage()); 43 | } 44 | } 45 | 46 | @Test 47 | public void shouldAllowChangeScriptsThatStartFromZero() throws Exception { 48 | ChangeScript zero = new ChangeScript(0); 49 | ChangeScript four = new ChangeScript(4); 50 | 51 | ChangeScriptRepository repository = new ChangeScriptRepository(Arrays.asList( zero, four )); 52 | 53 | List list = repository.getOrderedListOfDoChangeScripts(); 54 | assertThat(2, equalTo(list.size())); 55 | assertSame(zero, list.get(0)); 56 | assertSame(four, list.get(1)); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/java/com/dbdeploy/scripts/ChangeScriptTest.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.scripts; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.BufferedWriter; 6 | import java.io.File; 7 | import java.io.FileWriter; 8 | import java.io.IOException; 9 | 10 | import static org.hamcrest.Matchers.*; 11 | import static org.junit.Assert.assertThat; 12 | 13 | public class ChangeScriptTest { 14 | 15 | @Test 16 | public void changeScriptsHaveAnIdAndAFileAndEncoding() throws Exception { 17 | File file = new File("abc.txt"); 18 | ChangeScript changeScript = new ChangeScript(5, file, "UTF-8"); 19 | assertThat(changeScript.getId(), equalTo(5L)); 20 | assertThat(changeScript.getFile(), sameInstance(file)); 21 | } 22 | 23 | @Test 24 | public void shouldReturnContentsOfFile() throws Exception { 25 | File file = createTemporaryFileWithContent("Hello\nThere!\n"); 26 | 27 | ChangeScript changeScript = new ChangeScript(5, file, "UTF-8"); 28 | assertThat(changeScript.getContent(), is("Hello\nThere!\n")); 29 | } 30 | 31 | @Test 32 | public void contentsOfFileShouldExcludeAnythingAfterAnUndoMarker() throws Exception { 33 | File file = createTemporaryFileWithContent( 34 | "Hello\n" + 35 | "There!\n" + 36 | "--//@UNDO\n" + 37 | "This is after the undo marker!\n"); 38 | 39 | ChangeScript changeScript = new ChangeScript(5, file, "UTF-8"); 40 | assertThat(changeScript.getContent(), is("Hello\nThere!\n")); 41 | } 42 | 43 | @Test 44 | public void contentsOfFileShouldExcludeAnythingAfterAnUndoMarkerEvenWhenThatMarkerHasSomeWhitespaceAtTheEnd() throws Exception { 45 | File file = createTemporaryFileWithContent( 46 | "Hello\n" + 47 | "There!\n" + 48 | "--//@UNDO \n" + 49 | "This is after the undo marker!\n"); 50 | 51 | ChangeScript changeScript = new ChangeScript(5, file, "UTF-8"); 52 | assertThat(changeScript.getContent(), is("Hello\nThere!\n")); 53 | } 54 | 55 | @Test 56 | public void shouldReturnUndoContentsOfFile() throws Exception { 57 | File file = createTemporaryFileWithContent( 58 | "Hello\n" + 59 | "There!\n" + 60 | "--//@UNDO\n" + 61 | "This is after the undo marker!\n"); 62 | 63 | ChangeScript changeScript = new ChangeScript(5, file, "UTF-8"); 64 | assertThat(changeScript.getUndoContent(), is("This is after the undo marker!\n")); 65 | } 66 | 67 | private File createTemporaryFileWithContent(String content) throws IOException { 68 | File file = File.createTempFile("changeScriptTest", ".sql"); 69 | file.deleteOnExit(); 70 | 71 | BufferedWriter out = new BufferedWriter(new FileWriter(file)); 72 | out.write(content); 73 | out.close(); 74 | return file; 75 | } 76 | 77 | @Test 78 | public void changeScriptsNaturallyOrderById() throws Exception { 79 | ChangeScript one = new ChangeScript(1); 80 | ChangeScript two = new ChangeScript(2); 81 | 82 | assertThat(one.compareTo(two), lessThan(1)); 83 | assertThat(two.compareTo(one), greaterThanOrEqualTo(1)); 84 | } 85 | 86 | @Test 87 | public void toStringReturnsASensibleValue() throws Exception { 88 | File file = new File("abc.txt"); 89 | ChangeScript changeScript = new ChangeScript(5, file, "UTF-8"); 90 | assertThat(changeScript.toString(), equalTo("#5: abc.txt")); 91 | 92 | changeScript = new ChangeScript(5, "abc.txt"); 93 | assertThat(changeScript.toString(), equalTo("#5: abc.txt")); 94 | 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/java/com/dbdeploy/scripts/FilenameParserTest.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.scripts; 2 | 3 | import com.dbdeploy.exceptions.UnrecognisedFilenameException; 4 | import static org.junit.Assert.*; 5 | import org.junit.Test; 6 | 7 | public class FilenameParserTest { 8 | 9 | @Test 10 | public void canParseAnyFilenameThatStartsWithANumber() throws Exception { 11 | FilenameParser parser = new FilenameParser(); 12 | assertEquals(1L, parser.extractIdFromFilename("0001_a_filename.txt")); 13 | assertEquals(1L, parser.extractIdFromFilename("1_a_filename.txt")); 14 | assertEquals(1L, parser.extractIdFromFilename("1 a filename.txt")); 15 | assertEquals(1L, parser.extractIdFromFilename("1.txt")); 16 | assertEquals(123L, parser.extractIdFromFilename("00123_something.txt")); 17 | } 18 | 19 | @Test 20 | public void throwsWhenFilenameDoesNotStartWithANumber() throws Exception { 21 | FilenameParser parser = new FilenameParser(); 22 | try { 23 | parser.extractIdFromFilename("blah blah blah"); 24 | fail("expected exception"); 25 | } catch (UnrecognisedFilenameException e) { 26 | assertEquals("Could not extract a change script number from filename: blah blah blah", e.getMessage() ); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/java/com/dbdeploy/scripts/StubChangeScript.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.scripts; 2 | 3 | public class StubChangeScript extends ChangeScript { 4 | private final String changeContents; 5 | 6 | public StubChangeScript(int changeNumber, String description, String changeContents) { 7 | super(changeNumber, description); 8 | this.changeContents = changeContents; 9 | } 10 | 11 | @Override 12 | public String getContent() { 13 | return changeContents; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/resources/com/dbdeploy/database/db2_expected.sql: -------------------------------------------------------------------------------- 1 | 2 | -- START CHANGE SCRIPT #1: 001_change.sql 3 | 4 | -- contents of change script 1 5 | 6 | INSERT INTO changelog (change_number, complete_dt, applied_by, description) 7 | VALUES (1, CURRENT TIMESTAMP, USER, '001_change.sql'); 8 | 9 | COMMIT; 10 | 11 | -- END CHANGE SCRIPT #1: 001_change.sql 12 | 13 | 14 | -- START CHANGE SCRIPT #2: 002_change.sql 15 | 16 | -- contents of change script 2 17 | 18 | INSERT INTO changelog (change_number, complete_dt, applied_by, description) 19 | VALUES (2, CURRENT TIMESTAMP, USER, '002_change.sql'); 20 | 21 | COMMIT; 22 | 23 | -- END CHANGE SCRIPT #2: 002_change.sql 24 | 25 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/resources/com/dbdeploy/database/hsql_expected.sql: -------------------------------------------------------------------------------- 1 | 2 | -- START CHANGE SCRIPT #1: 001_change.sql 3 | 4 | -- contents of change script 1 5 | 6 | INSERT INTO changelog (change_number, complete_dt, applied_by, description) 7 | VALUES (1, CURRENT_TIMESTAMP, USER(), '001_change.sql'); 8 | 9 | COMMIT; 10 | 11 | -- END CHANGE SCRIPT #1: 001_change.sql 12 | 13 | 14 | -- START CHANGE SCRIPT #2: 002_change.sql 15 | 16 | -- contents of change script 2 17 | 18 | INSERT INTO changelog (change_number, complete_dt, applied_by, description) 19 | VALUES (2, CURRENT_TIMESTAMP, USER(), '002_change.sql'); 20 | 21 | COMMIT; 22 | 23 | -- END CHANGE SCRIPT #2: 002_change.sql 24 | 25 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/resources/com/dbdeploy/database/mssql_expected.sql: -------------------------------------------------------------------------------- 1 | 2 | -- START CHANGE SCRIPT #1: 001_change.sql 3 | 4 | -- contents of change script 1 5 | 6 | INSERT INTO changelog (change_number, complete_dt, applied_by, description) 7 | VALUES (1, getdate(), user_name(), '001_change.sql') 8 | GO 9 | 10 | COMMIT 11 | GO 12 | 13 | -- END CHANGE SCRIPT #1: 001_change.sql 14 | 15 | 16 | -- START CHANGE SCRIPT #2: 002_change.sql 17 | 18 | -- contents of change script 2 19 | 20 | INSERT INTO changelog (change_number, complete_dt, applied_by, description) 21 | VALUES (2, getdate(), user_name(), '002_change.sql') 22 | GO 23 | 24 | COMMIT 25 | GO 26 | 27 | -- END CHANGE SCRIPT #2: 002_change.sql 28 | 29 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/resources/com/dbdeploy/database/mysql_expected.sql: -------------------------------------------------------------------------------- 1 | 2 | -- START CHANGE SCRIPT #1: 001_change.sql 3 | 4 | -- contents of change script 1 5 | 6 | INSERT INTO changelog (change_number, complete_dt, applied_by, description) 7 | VALUES (1, CURRENT_TIMESTAMP, USER(), '001_change.sql'); 8 | 9 | COMMIT; 10 | 11 | -- END CHANGE SCRIPT #1: 001_change.sql 12 | 13 | 14 | -- START CHANGE SCRIPT #2: 002_change.sql 15 | 16 | -- contents of change script 2 17 | 18 | INSERT INTO changelog (change_number, complete_dt, applied_by, description) 19 | VALUES (2, CURRENT_TIMESTAMP, USER(), '002_change.sql'); 20 | 21 | COMMIT; 22 | 23 | -- END CHANGE SCRIPT #2: 002_change.sql 24 | 25 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/resources/com/dbdeploy/database/ora_expected.sql: -------------------------------------------------------------------------------- 1 | 2 | -- START CHANGE SCRIPT #1: 001_change.sql 3 | 4 | -- contents of change script 1 5 | 6 | INSERT INTO changelog (change_number, complete_dt, applied_by, description) 7 | VALUES (1, CURRENT_TIMESTAMP, USER, '001_change.sql'); 8 | 9 | COMMIT; 10 | 11 | -- END CHANGE SCRIPT #1: 001_change.sql 12 | 13 | 14 | -- START CHANGE SCRIPT #2: 002_change.sql 15 | 16 | -- contents of change script 2 17 | 18 | INSERT INTO changelog (change_number, complete_dt, applied_by, description) 19 | VALUES (2, CURRENT_TIMESTAMP, USER, '002_change.sql'); 20 | 21 | COMMIT; 22 | 23 | -- END CHANGE SCRIPT #2: 002_change.sql 24 | 25 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/resources/com/dbdeploy/database/pgsql_expected.sql: -------------------------------------------------------------------------------- 1 | 2 | -- START CHANGE SCRIPT #1: 001_change.sql 3 | 4 | -- contents of change script 1 5 | 6 | INSERT INTO changelog (change_number, complete_dt, applied_by, description) 7 | VALUES (1, current_timestamp, current_user, '001_change.sql'); 8 | 9 | COMMIT; 10 | 11 | -- END CHANGE SCRIPT #1: 001_change.sql 12 | 13 | 14 | -- START CHANGE SCRIPT #2: 002_change.sql 15 | 16 | -- contents of change script 2 17 | 18 | INSERT INTO changelog (change_number, complete_dt, applied_by, description) 19 | VALUES (2, current_timestamp, current_user, '002_change.sql'); 20 | 21 | COMMIT; 22 | 23 | -- END CHANGE SCRIPT #2: 002_change.sql 24 | 25 | -------------------------------------------------------------------------------- /dbdeploy-core/src/test/resources/com/dbdeploy/database/syb-ase_expected.sql: -------------------------------------------------------------------------------- 1 | 2 | -- START CHANGE SCRIPT #1: 001_change.sql 3 | 4 | -- contents of change script 1 5 | 6 | INSERT INTO changelog (change_number, complete_dt, applied_by, description) 7 | VALUES (1, getdate(), user_name(), '001_change.sql') 8 | GO 9 | 10 | COMMIT 11 | GO 12 | 13 | -- END CHANGE SCRIPT #1: 001_change.sql 14 | 15 | 16 | -- START CHANGE SCRIPT #2: 002_change.sql 17 | 18 | -- contents of change script 2 19 | 20 | INSERT INTO changelog (change_number, complete_dt, applied_by, description) 21 | VALUES (2, getdate(), user_name(), '002_change.sql') 22 | GO 23 | 24 | COMMIT 25 | GO 26 | 27 | -- END CHANGE SCRIPT #2: 002_change.sql 28 | 29 | -------------------------------------------------------------------------------- /dbdeploy-dist/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | com.dbdeploy 6 | dbdeploy-parent 7 | 3.0-SNAPSHOT 8 | 9 | 10 | dbdeploy-dist 11 | pom 12 | dbdeploy-dist 13 | 14 | 15 | 16 | 17 | 18 | 19 | maven-assembly-plugin 20 | 21 | 22 | create-distribution 23 | package 24 | 25 | single 26 | 27 | 28 | 29 | src/main/resources/assemblies/distribution.xml 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 42 | maven-deploy-plugin 43 | 44 | true 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | ${project.groupId} 56 | dbdeploy-ant 57 | ${project.version} 58 | 59 | 60 | 61 | ${project.groupId} 62 | dbdeploy-cli 63 | ${project.version} 64 | 65 | 66 | 67 | ${project.groupId} 68 | dbdeploy-core 69 | ${project.version} 70 | 71 | 72 | 73 | ${project.groupId} 74 | dbdeploy-ant 75 | ${project.version} 76 | sources 77 | 78 | 79 | 80 | ${project.groupId} 81 | dbdeploy-cli 82 | ${project.version} 83 | sources 84 | 85 | 86 | 87 | ${project.groupId} 88 | dbdeploy-core 89 | ${project.version} 90 | sources 91 | 92 | 93 | 94 | hsqldb 95 | hsqldb 96 | ${hsqldb.version} 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /dbdeploy-dist/src/main/doc/LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /dbdeploy-dist/src/main/doc/README: -------------------------------------------------------------------------------- 1 | Welcome to dbdeploy! 2 | ==================== 3 | 4 | General information about the concepts behind dbdeploy can be found at: 5 | 6 | http://dbdeploy.com 7 | 8 | Information specific to this java version of dbdeploy can be found at: 9 | 10 | http://code.google.com/p/dbdeploy/ 11 | 12 | -------------------------------------------------------------------------------- /dbdeploy-dist/src/main/example/001_create_table.sql: -------------------------------------------------------------------------------- 1 | 2 | -- Most databases don't apply DDL statements transactionally. 3 | -- Therefore, to recover from failure more easily, only put a single DDL 4 | -- statement in each change script. 5 | 6 | CREATE TABLE Test (id INTEGER, data VARCHAR(100)); 7 | 8 | --//@UNDO 9 | 10 | DROP TABLE Test; -------------------------------------------------------------------------------- /dbdeploy-dist/src/main/example/002_insert_data.sql: -------------------------------------------------------------------------------- 1 | -- dbdeploy will wrap the application of each change script 2 | -- in a transaction 3 | -- 4 | -- DML statements (INSERT, UPDATE etc) can be applied transactionally, 5 | -- so therefore dbdeploy will ensure that either the whole of this script applies 6 | -- or none of it does. 7 | -- 8 | INSERT INTO Test VALUES (6, 'This is simple text'); 9 | INSERT INTO Test VALUES (7, 'Some UTF-8 chars: åßéá'); 10 | 11 | --//@UNDO 12 | 13 | DELETE FROM Test WHERE id IN (6,7); 14 | 15 | -------------------------------------------------------------------------------- /dbdeploy-dist/src/main/example/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 45 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 64 | 65 | 66 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 76 | select * from changelog; 77 | select * from test; 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /dbdeploy-dist/src/main/example/cli_example.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This is an example of invoking the command line interface of dbdeploy 4 | # Typically, this is used on production deployments where there is a 5 | # desire not to have an ant installation on the target machine. 6 | 7 | # To try this script, you need to run "ant clean" first to create an 8 | # empty database with a blank dbdeploy changelog table. 9 | 10 | set ../dbdeploy-cli-*.jar hsqldb*.jar 11 | CP=$(IFS=:; echo "$*") 12 | echo "Classpath is $CP" 13 | 14 | java -cp $CP com.dbdeploy.CommandLineTarget \ 15 | --driver "org.hsqldb.jdbcDriver" \ 16 | --url "jdbc:hsqldb:file:db/testdb;shutdown=true" \ 17 | --userid "sa" \ 18 | --password "" 19 | 20 | # NB: if you don't want to include the database password on the 21 | # command line (as you probably don't), miss out the parameter to 22 | # --password and dbdeploy will then read from stdin. You can then 23 | # do "echo password | java -cp $CP com.dbdeploy...." 24 | 25 | -------------------------------------------------------------------------------- /dbdeploy-dist/src/main/example/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | dbdeploy-test 5 | com.dbdeploy 6 | 1.0-SNAPSHOT 7 | dbdeploy Maven example 8 | Tests Dbdeploy Plugin 9 | 10 | 11 | 12 | 13 | com.dbdeploy 14 | maven-dbdeploy-plugin 15 | 3.0-SNAPSHOT 16 | 17 | 18 | . 19 | org.hsqldb.jdbcDriver 20 | jdbc:hsqldb:file:db/testdb;shutdown=true 21 | sa 22 | 23 | hsql 24 | ; 25 | row 26 | apply.sql 27 | undo.sql 28 | 29 | 30 | 31 | 32 | com.dbdeploy 33 | dbdeploy-core 34 | 3.0-SNAPSHOT 35 | 36 | 37 | hsqldb 38 | hsqldb 39 | 1.8.0.7 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /dbdeploy-dist/src/main/resources/assemblies/distribution.xml: -------------------------------------------------------------------------------- 1 | 2 | distribution 3 | 4 | zip 5 | 6 | 7 | dbdeploy-${project.version} 8 | 9 | 10 | 11 | false 12 | false 13 | / 14 | 15 | ${project.groupId}:* 16 | 17 | 18 | 19 | 20 | false 21 | false 22 | example 23 | 24 | hsqldb:* 25 | 26 | 27 | 28 | 29 | 30 | 31 | src/main/doc 32 | / 33 | 34 | 35 | 36 | src/main/example 37 | example 38 | 39 | 40 | 41 | src/main/scripts 42 | scripts 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /dbdeploy-dist/src/main/scripts/createSchemaVersionTable.db2.sql: -------------------------------------------------------------------------------- 1 | 2 | CREATE TABLE changelog ( 3 | change_number NUMERIC(22,0) NOT NULL, 4 | complete_dt TIMESTAMP NOT NULL, 5 | applied_by VARCHAR(100) NOT NULL, 6 | description VARCHAR(500) NOT NULL 7 | ); 8 | 9 | ALTER TABLE changelog ADD CONSTRAINT Pkchangelog PRIMARY KEY (change_number); 10 | -------------------------------------------------------------------------------- /dbdeploy-dist/src/main/scripts/createSchemaVersionTable.hsql.sql: -------------------------------------------------------------------------------- 1 | 2 | CREATE TABLE changelog ( 3 | change_number BIGINT NOT NULL, 4 | complete_dt TIMESTAMP NOT NULL, 5 | applied_by VARCHAR(100) NOT NULL, 6 | description VARCHAR(500) NOT NULL 7 | ); 8 | 9 | ALTER TABLE changelog ADD CONSTRAINT Pkchangelog PRIMARY KEY (change_number) 10 | ; -------------------------------------------------------------------------------- /dbdeploy-dist/src/main/scripts/createSchemaVersionTable.mssql.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE changelog ( 2 | change_number BIGINT NOT NULL, 3 | complete_dt DATETIME NOT NULL, 4 | applied_by VARCHAR(100) NOT NULL, 5 | description VARCHAR(500) NOT NULL 6 | ) 7 | GO 8 | 9 | ALTER TABLE changelog ADD CONSTRAINT Pkchangelog PRIMARY KEY (change_number) 10 | GO -------------------------------------------------------------------------------- /dbdeploy-dist/src/main/scripts/createSchemaVersionTable.mysql.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE changelog ( 2 | change_number BIGINT NOT NULL, 3 | complete_dt TIMESTAMP NOT NULL, 4 | applied_by VARCHAR(100) NOT NULL, 5 | description VARCHAR(500) NOT NULL 6 | ); 7 | 8 | ALTER TABLE changelog ADD CONSTRAINT Pkchangelog PRIMARY KEY (change_number) 9 | ; -------------------------------------------------------------------------------- /dbdeploy-dist/src/main/scripts/createSchemaVersionTable.ora.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE changelog ( 2 | change_number NUMBER(22,0) NOT NULL, 3 | complete_dt TIMESTAMP NOT NULL, 4 | applied_by VARCHAR2(100) NOT NULL, 5 | description VARCHAR2(500) NOT NULL 6 | ); 7 | 8 | ALTER TABLE changelog ADD CONSTRAINT Pkchangelog PRIMARY KEY (change_number) 9 | ; -------------------------------------------------------------------------------- /dbdeploy-dist/src/main/scripts/createSchemaVersionTable.syb-ase.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE changelog ( 2 | change_number NUMERIC(22,0) NOT NULL, 3 | complete_dt DATETIME NOT NULL, 4 | applied_by VARCHAR(100) NOT NULL, 5 | description VARCHAR(500) NOT NULL 6 | ) 7 | GO 8 | 9 | ALTER TABLE changelog ADD CONSTRAINT Pkchangelog PRIMARY KEY (change_number) 10 | GO -------------------------------------------------------------------------------- /maven-dbdeploy-plugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | dbdeploy-parent 7 | com.dbdeploy 8 | 3.0-SNAPSHOT 9 | 10 | 11 | maven-dbdeploy-plugin 12 | maven-dbdeploy-plugin 13 | maven-plugin 14 | maven plugin to invoke dbdeploy 15 | 16 | 17 | 18 | 19 | org.apache.maven.plugins 20 | maven-source-plugin 21 | 22 | 23 | package 24 | 25 | jar 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.apache.maven 36 | maven-plugin-api 37 | 2.0 38 | 39 | 40 | ${project.groupId} 41 | dbdeploy-core 42 | ${project.version} 43 | 44 | 45 | junit 46 | junit 47 | 3.8.1 48 | test 49 | 50 | 51 | org.apache.maven.shared 52 | maven-plugin-testing-harness 53 | 1.1 54 | test 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /maven-dbdeploy-plugin/src/main/java/com/dbdeploy/mojo/AbstractDbDeployMojo.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.mojo; 2 | 3 | /* 4 | * Copyright 2001-2005 The Apache Software Foundation. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | import com.dbdeploy.DbDeploy; 19 | import com.dbdeploy.database.DelimiterType; 20 | import com.dbdeploy.database.LineEnding; 21 | import org.apache.maven.plugin.AbstractMojo; 22 | 23 | import java.io.File; 24 | 25 | /** 26 | * Abstract class that all dbdeploy database goals should extend. 27 | */ 28 | public abstract class AbstractDbDeployMojo extends AbstractMojo { 29 | /** 30 | * Full or relative path to the directory containing the delta scripts. 31 | * 32 | * @parameter expression="${dbdeploy.scriptdirectory}" default-value="${project.src.directory}/main/sql" 33 | * @required 34 | */ 35 | protected File scriptdirectory; 36 | 37 | /** 38 | * Encoding to use for change scripts and output files. 39 | * 40 | * @parameter expression="${dbdeploy.encoding}" default-value="${project.build.sourceEncoding}" 41 | */ 42 | protected String encoding; 43 | 44 | 45 | /** 46 | * Specifies the jdbc driver. 47 | * 48 | * @parameter expression="${dbdeploy.driver}" 49 | * @required 50 | */ 51 | protected String driver; 52 | 53 | /** 54 | * Specifies the url of the database that the deltas are to be applied to. 55 | * 56 | * @parameter expression="${dbdeploy.url}" 57 | * @required 58 | */ 59 | protected String url; 60 | 61 | /** 62 | * The password of the dbms user who has permissions to select from the 63 | * schema version table. 64 | * 65 | * @parameter expression="${dbdeploy.password}" 66 | */ 67 | protected String password; 68 | 69 | /** 70 | * The ID of a dbms user who has permissions to select from the schema 71 | * version table. 72 | * 73 | * @parameter expression="${dbdeploy.userid}" 74 | * @required 75 | */ 76 | protected String userid; 77 | 78 | /** 79 | * The name of the changelog table to use. Useful if you need to separate 80 | * DDL and DML when deploying to replicated environments. If not supplied 81 | * defaults to "changelog" 82 | * 83 | * @parameter expression="${dbdeploy.changeLogTableName}" 84 | */ 85 | protected String changeLogTableName; 86 | 87 | /** 88 | * Delimiter to use to separate scripts into statements, if dbdeploy will 89 | * apply the scripts for you i.e. you haven't specified outputfile. Default ; 90 | * 91 | * @parameter expression="${dbdeploy.delimiter}" 92 | */ 93 | protected String delimiter; 94 | 95 | /** 96 | * Either normal: split on delimiter wherever it occurs or row only split 97 | * on delimiter if it features on a line by itself. Default normal. 98 | * 99 | * @parameter expression="${dbdeploy.delimiterType}" 100 | */ 101 | protected String delimiterType; 102 | 103 | /** 104 | * Line ending to separate indiviual statement lines when applying directly 105 | * to the database. Can be platform (the default line ending for the current platform), 106 | * cr, crlf or lf. Default platform. 107 | * 108 | * @parameter expression="${dbdeploy.lineEnding}" 109 | */ 110 | protected String lineEnding; 111 | 112 | /** 113 | * The highest numbered delta script to apply. 114 | * 115 | * @parameter expression="${dbdeploy.lastChange}" 116 | */ 117 | protected Long lastChangeToApply; 118 | 119 | protected DbDeploy getConfiguredDbDeploy() { 120 | DbDeploy dbDeploy = new DbDeploy(); 121 | dbDeploy.setScriptdirectory(scriptdirectory); 122 | dbDeploy.setDriver(driver); 123 | dbDeploy.setUrl(url); 124 | dbDeploy.setPassword(password); 125 | dbDeploy.setUserid(userid); 126 | 127 | if (encoding != null) { 128 | dbDeploy.setEncoding(encoding); 129 | } 130 | 131 | if (lastChangeToApply != null) { 132 | dbDeploy.setLastChangeToApply(lastChangeToApply); 133 | } 134 | 135 | if (changeLogTableName != null) { 136 | dbDeploy.setChangeLogTableName(changeLogTableName); 137 | } 138 | 139 | if (delimiter != null) { 140 | dbDeploy.setDelimiter(delimiter); 141 | } 142 | 143 | if (delimiterType != null) { 144 | dbDeploy.setDelimiterType(DelimiterType.valueOf(delimiterType)); 145 | } 146 | 147 | if (lineEnding != null) { 148 | dbDeploy.setLineEnding(LineEnding.valueOf(lineEnding)); 149 | } 150 | 151 | return dbDeploy; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /maven-dbdeploy-plugin/src/main/java/com/dbdeploy/mojo/CreateChangeScriptMojo.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.mojo; 2 | 3 | /* 4 | * Copyright 2001-2005 The Apache Software Foundation. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | import com.dbdeploy.scripts.ChangeScriptCreator; 19 | import java.io.File; 20 | import org.apache.maven.plugin.AbstractMojo; 21 | import org.apache.maven.plugin.MojoExecutionException; 22 | 23 | /** 24 | * Maven goal for creating a new timestamped dbdeploy change script. 25 | * 26 | * @goal change-script 27 | */ 28 | public class CreateChangeScriptMojo extends AbstractMojo { 29 | /** 30 | * Name suffix for the file that will be created (e.g. add_email_to_user_table). 31 | * 32 | * @parameter expression="${dbdeploy.script.name}" default-value="new_change_script" 33 | * @required 34 | */ 35 | private String name; 36 | 37 | /** 38 | * Directory where change scripts reside. 39 | * 40 | * @parameter expression="${dbdeploy.scriptdirectory}" default-value="${project.src.directory}/main/sql" 41 | * @required 42 | */ 43 | private File scriptdirectory; 44 | 45 | public void execute() throws MojoExecutionException { 46 | try { 47 | final ChangeScriptCreator changeScriptCreator = getConfiguredChangeScriptCreator(); 48 | final File newChangeScript = changeScriptCreator.go(); 49 | 50 | getLog().info("Created new change script:\n\t" + newChangeScript.getAbsolutePath()); 51 | } catch (Exception e) { 52 | getLog().error(e); 53 | throw new MojoExecutionException("create change script failed", e); 54 | } 55 | } 56 | 57 | private ChangeScriptCreator getConfiguredChangeScriptCreator() { 58 | final ChangeScriptCreator changeScriptCreator = new ChangeScriptCreator(); 59 | changeScriptCreator.setScriptDescription(name); 60 | changeScriptCreator.setScriptDirectory(scriptdirectory); 61 | 62 | return changeScriptCreator; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /maven-dbdeploy-plugin/src/main/java/com/dbdeploy/mojo/CreateDatabaseScriptsMojo.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.mojo; 2 | 3 | /* 4 | * Copyright 2001-2005 The Apache Software Foundation. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | import com.dbdeploy.DbDeploy; 19 | import org.apache.maven.plugin.MojoExecutionException; 20 | 21 | import java.io.File; 22 | 23 | /** 24 | * Maven goal for creating the apply and undo scripts. 25 | * 26 | * @goal db-scripts 27 | */ 28 | public class CreateDatabaseScriptsMojo extends AbstractDbDeployMojo { 29 | 30 | /** 31 | * The name of the script that dbdeploy will output. Include a full 32 | * or relative path. 33 | * 34 | * @parameter 35 | * @required 36 | */ 37 | private File outputfile; 38 | 39 | /** 40 | * String representing our DBMS (e.g. mysql, ora) 41 | * 42 | * @parameter 43 | * @required 44 | */ 45 | private String dbms; 46 | 47 | /** 48 | * The name of the undo script that dbdeploy will output. Include a full 49 | * or relative path. 50 | * 51 | * @parameter 52 | * @required 53 | */ 54 | private File undoOutputfile; 55 | 56 | /** 57 | * Directory for your template scripts, if not using built-in 58 | * 59 | * @parameter 60 | */ 61 | private File templateDirectory; 62 | 63 | public void execute() throws MojoExecutionException { 64 | DbDeploy dbDeploy = getConfiguredDbDeploy(); 65 | 66 | try { 67 | dbDeploy.go(); 68 | } catch (Exception e) { 69 | getLog().error(e); 70 | throw new MojoExecutionException("dbdeploy change script create failed", e); 71 | } 72 | } 73 | 74 | @Override 75 | protected DbDeploy getConfiguredDbDeploy() { 76 | DbDeploy dbDeploy = super.getConfiguredDbDeploy(); 77 | dbDeploy.setOutputfile(outputfile); 78 | dbDeploy.setUndoOutputfile(undoOutputfile); 79 | dbDeploy.setDbms(dbms); 80 | 81 | if (templateDirectory != null) { 82 | dbDeploy.setTemplatedir(templateDirectory); 83 | } 84 | 85 | return dbDeploy; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /maven-dbdeploy-plugin/src/main/java/com/dbdeploy/mojo/UpdateDatabaseMojo.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.mojo; 2 | 3 | /* 4 | * Copyright 2001-2005 The Apache Software Foundation. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | import com.dbdeploy.DbDeploy; 19 | import org.apache.maven.plugin.MojoExecutionException; 20 | 21 | 22 | /** 23 | * Maven goal for applying dbdeploy change scripts directly to the database. 24 | * 25 | * @goal update 26 | */ 27 | public class UpdateDatabaseMojo extends AbstractDbDeployMojo { 28 | 29 | public void execute() throws MojoExecutionException { 30 | DbDeploy dbDeploy = getConfiguredDbDeploy(); 31 | 32 | try { 33 | dbDeploy.go(); 34 | } catch (Exception e) { 35 | getLog().error(e); 36 | throw new MojoExecutionException("dbdeploy update failed", e); 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /maven-dbdeploy-plugin/src/test/java/com/dbdeploy/mojo/CreateChangeScriptMojoTest.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.mojo; 2 | 3 | /* 4 | * Copyright 2001-2005 The Apache Software Foundation. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | import java.io.File; 19 | import org.apache.maven.plugin.testing.AbstractMojoTestCase; 20 | 21 | public class CreateChangeScriptMojoTest extends AbstractMojoTestCase { 22 | 23 | @Override 24 | protected void setUp() throws Exception { 25 | super.setUp(); 26 | } 27 | 28 | public void testCreateChangeScriptConfiguration() throws Exception { 29 | File testPom = new File(getBasedir(), "src/test/resources/unit/test/create-change-script-plugin-config.xml"); 30 | 31 | CreateChangeScriptMojo mojo = (CreateChangeScriptMojo) lookupMojo("change-script", testPom); 32 | 33 | assertNotNull(mojo); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /maven-dbdeploy-plugin/src/test/java/com/dbdeploy/mojo/CreateDatabaseScriptsMojoTest.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.mojo; 2 | 3 | /* 4 | * Copyright 2001-2005 The Apache Software Foundation. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | import java.io.File; 19 | import org.apache.maven.plugin.testing.AbstractMojoTestCase; 20 | 21 | public class CreateDatabaseScriptsMojoTest extends AbstractMojoTestCase { 22 | 23 | @Override 24 | protected void setUp() throws Exception { 25 | super.setUp(); 26 | } 27 | 28 | public void testCreateChangeScriptConfiguration() throws Exception { 29 | File testPom = new File(getBasedir(), "src/test/resources/unit/test/db-scripts-plugin-config.xml"); 30 | 31 | CreateDatabaseScriptsMojo mojo = (CreateDatabaseScriptsMojo) lookupMojo("db-scripts", testPom); 32 | 33 | assertNotNull(mojo); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /maven-dbdeploy-plugin/src/test/java/com/dbdeploy/mojo/UpdateDatabaseMojoTest.java: -------------------------------------------------------------------------------- 1 | package com.dbdeploy.mojo; 2 | 3 | /* 4 | * Copyright 2001-2005 The Apache Software Foundation. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | import java.io.File; 19 | import org.apache.maven.plugin.testing.AbstractMojoTestCase; 20 | 21 | public class UpdateDatabaseMojoTest extends AbstractMojoTestCase { 22 | 23 | @Override 24 | protected void setUp() throws Exception { 25 | super.setUp(); 26 | } 27 | 28 | public void testCreateChangeScriptConfiguration() throws Exception { 29 | File testPom = new File(getBasedir(), "src/test/resources/unit/test/update-plugin-config.xml"); 30 | 31 | UpdateDatabaseMojo mojo = (UpdateDatabaseMojo) lookupMojo("update", testPom); 32 | 33 | assertNotNull(mojo); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /maven-dbdeploy-plugin/src/test/resources/unit/test/create-change-script-plugin-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | maven-dbdeploy-plugin 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /maven-dbdeploy-plugin/src/test/resources/unit/test/db-scripts-plugin-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | maven-dbdeploy-plugin 6 | 7 | . 8 | org.hsqldb.jdbcDriver 9 | jdbc:hsqldb:file:db/testdb;shutdown=true 10 | sa 11 | 12 | hsql 13 | apply.sql 14 | undo.sql 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /maven-dbdeploy-plugin/src/test/resources/unit/test/update-plugin-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | maven-dbdeploy-plugin 6 | 7 | . 8 | org.hsqldb.jdbcDriver 9 | jdbc:hsqldb:file:db/testdb;shutdown=true 10 | sa 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.dbdeploy 4 | dbdeploy-parent 5 | pom 6 | 3.0-SNAPSHOT 7 | dbdeploy 8 | dbdeploy: java tools to manage agile database management 9 | http://www.dbdeploy.com 10 | 11 | 12 | org.sonatype.oss 13 | oss-parent 14 | 6 15 | 16 | 17 | 18 | dbdeploy-core 19 | dbdeploy-ant 20 | dbdeploy-cli 21 | maven-dbdeploy-plugin 22 | dbdeploy-dist 23 | 24 | 25 | 26 | 33 | http://github.com/tackley/dbdeploy 34 | scm:git:git://github.com/tackley/dbdeploy.git 35 | scm:git:git@github.com:tackley/dbdeploy.git 36 | 37 | 38 | 39 | 40 | LGPL 41 | 42 | 43 | 44 | 45 | 46 | users 47 | db-deploy-users@googlegroups.com 48 | http://groups.google.com/group/db-deploy-users 49 | 50 | 51 | 52 | 53 | 54 | Graham Tackley 55 | graham@tackley.net 56 | http://blog.tackley.net 57 | 0 58 | 59 | 60 | 61 | 62 | 1.8.0.7 63 | 64 | 65 | 66 | 67 | 68 | 69 | org.apache.maven.plugins 70 | maven-compiler-plugin 71 | 72 | 1.5 73 | 1.5 74 | UTF-8 75 | 76 | 77 | 78 | 79 | org.apache.maven.plugins 80 | maven-resources-plugin 81 | 82 | UTF-8 83 | 84 | 85 | 86 | 87 | org.apache.maven.plugins 88 | maven-shade-plugin 89 | 1.4 90 | 91 | 92 | 93 | org.apache.maven.plugins 94 | maven-source-plugin 95 | 2.1.2 96 | 97 | 98 | 99 | 100 | org.apache.maven.plugins 101 | maven-jar-plugin 102 | 103 | 104 | 105 | true 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | org.freemarker 120 | freemarker 121 | 2.3.15 122 | 123 | 124 | 125 | junit 126 | junit 127 | 4.4 128 | test 129 | 130 | 131 | 132 | org.hamcrest 133 | hamcrest-all 134 | 1.1 135 | test 136 | 137 | 138 | 139 | org.mockito 140 | mockito-all 141 | 1.7 142 | test 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /todo.txt: -------------------------------------------------------------------------------- 1 | dbdeploy todo: 2 | 3 | 1. implement maven plugin 4 | 5 | 2. implement line endings parameter 6 | 7 | 3. publish new snapshot to oss.sonatype 8 | 9 | 4. add a sensible readme for github 10 | 11 | 4. release new milestone release to sonatype 12 | -------------------------------------------------------------------------------- /validate_distribution_examples.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mvn clean install 4 | 5 | if [ "$?" -ne "0" ]; then 6 | echo "Hmm. Build failed." 7 | exit 99 8 | fi 9 | 10 | TMPDIR=$(mktemp -d -t dbdeploy) 11 | 12 | unzip dbdeploy-dist/target/dbdeploy-dist-*-distribution.zip -d $TMPDIR 13 | 14 | cd $TMPDIR/dbdeploy-*/example/ 15 | 16 | ant default 17 | 18 | # for some reason, the sql task doesn't display the output 19 | # if it's run in the same session as the updates. 20 | ant dump-tables 21 | 22 | mvn dbdeploy:change-script -Ddbdeploy.script.name=add_column_to_test_table 23 | 24 | mvn dbdeploy:db-scripts 25 | 26 | echo "Created apply.sql file..." 27 | cat apply.sql 28 | 29 | echo "Created undo.sql file..." 30 | cat undo.sql 31 | 32 | mvn dbdeploy:update 33 | 34 | ant dump-tables 35 | 36 | mvn dbdeploy:update -Ddbdeploy.lastChange=1 37 | 38 | ant dump-tables 39 | --------------------------------------------------------------------------------