diff --git a/.gitignore b/.gitignore index d6c3350..866120e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ target *.iws .idea *.externalToolBuilders +*.bak +*.tmp diff --git a/pom.xml b/pom.xml index ceb71a5..9e4dd57 100644 --- a/pom.xml +++ b/pom.xml @@ -276,12 +276,12 @@ - commons-lang - commons-lang - [2.0,) + org.apache.commons + commons-lang3 + [3.0,) compile - + commons-logging commons-logging diff --git a/src/main/java/com/tacitknowledge/util/migration/DistributedMigrationProcess.java b/src/main/java/com/tacitknowledge/util/migration/DistributedMigrationProcess.java index 7980955..b76ff59 100644 --- a/src/main/java/com/tacitknowledge/util/migration/DistributedMigrationProcess.java +++ b/src/main/java/com/tacitknowledge/util/migration/DistributedMigrationProcess.java @@ -436,9 +436,16 @@ public final LinkedHashMap getMigrationTasksWithLaunchers() throws MigrationExce if (log.isDebugEnabled()) { Iterator launchers = subLauncher.getContexts().keySet().iterator(); - String systemName = ((JdbcMigrationContext) launchers.next()).getSystemName(); - log.debug("\tMigration+Launcher binder found subtask " + task.getName() + if (!launchers.hasNext()) { + log.debug("\tMigration+Launcher binder found subtask " + task.getName() + + " with no contexts defined"); + + } else { + JdbcMigrationContext next = (JdbcMigrationContext) launchers.next(); + String systemName = next.getSystemName(); + log.debug("\tMigration+Launcher binder found subtask " + task.getName() + " for launcher context " + systemName); + } } // store the task, related to its launcher diff --git a/src/main/java/com/tacitknowledge/util/migration/MigrationRunnerFactory.java b/src/main/java/com/tacitknowledge/util/migration/MigrationRunnerFactory.java index 440ff9c..4d85561 100644 --- a/src/main/java/com/tacitknowledge/util/migration/MigrationRunnerFactory.java +++ b/src/main/java/com/tacitknowledge/util/migration/MigrationRunnerFactory.java @@ -15,7 +15,7 @@ package com.tacitknowledge.util.migration; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -35,23 +35,24 @@ public class MigrationRunnerFactory public static MigrationRunnerStrategy getMigrationRunnerStrategy(String strategy) { - log.info("Strategy received '" + strategy + "'"); if (StringUtils.isBlank(strategy)) { + log.info("No migration strategy specified. Using default: '" + DEFAULT_MIGRATION_STRATEGY + "'"); return new OrderedMigrationRunnerStrategy(); } try { + log.info("Using specified migration strategy: '" + strategy + "'"); Class c = Class.forName(strategy.trim()); MigrationRunnerStrategy runnerStrategy = (MigrationRunnerStrategy) c.newInstance(); return runnerStrategy; } catch (Exception e) { - throw new IllegalArgumentException("Strategy selected " + strategy + " cannot be instantiated ", e); + throw new IllegalArgumentException("Specified strategy (" + strategy + ") cannot be instantiated ", e); } diff --git a/src/main/java/com/tacitknowledge/util/migration/MissingPatchMigrationRunnerStrategy.java b/src/main/java/com/tacitknowledge/util/migration/MissingPatchMigrationRunnerStrategy.java index 44f9d33..6dfef07 100644 --- a/src/main/java/com/tacitknowledge/util/migration/MissingPatchMigrationRunnerStrategy.java +++ b/src/main/java/com/tacitknowledge/util/migration/MissingPatchMigrationRunnerStrategy.java @@ -15,7 +15,7 @@ package com.tacitknowledge.util.migration; -import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang3.ArrayUtils; import java.util.ArrayList; import java.util.Arrays; @@ -38,6 +38,7 @@ public boolean shouldMigrationRun(int migrationLevel, PatchInfoStore patchInfoSt throw new IllegalArgumentException("Patch Info Store should not be null"); } + // This is the crux of this migration strategy. As long as a patch has not yet been executed, it can be. return !patchInfoStore.isPatchApplied(migrationLevel); } diff --git a/src/main/java/com/tacitknowledge/util/migration/OrderedMigrationRunnerStrategy.java b/src/main/java/com/tacitknowledge/util/migration/OrderedMigrationRunnerStrategy.java index cafe706..6091d8b 100644 --- a/src/main/java/com/tacitknowledge/util/migration/OrderedMigrationRunnerStrategy.java +++ b/src/main/java/com/tacitknowledge/util/migration/OrderedMigrationRunnerStrategy.java @@ -34,6 +34,7 @@ public class OrderedMigrationRunnerStrategy implements MigrationRunnerStrategy { public boolean shouldMigrationRun(int migrationLevel, PatchInfoStore patchInfoStore) throws MigrationException { + // This is the crux of this migration strategy. Only patches with a patch level higher than the highest one executed so far are eligible to be excuted. return migrationLevel > patchInfoStore.getPatchLevel(); } diff --git a/src/main/java/com/tacitknowledge/util/migration/jdbc/AutoPatchSupport.java b/src/main/java/com/tacitknowledge/util/migration/jdbc/AutoPatchSupport.java index fdaed71..081fdd3 100644 --- a/src/main/java/com/tacitknowledge/util/migration/jdbc/AutoPatchSupport.java +++ b/src/main/java/com/tacitknowledge/util/migration/jdbc/AutoPatchSupport.java @@ -51,6 +51,7 @@ public class AutoPatchSupport */ public AutoPatchSupport(String systemName) throws MigrationException { + //TODO should be injected this(new JdbcMigrationLauncherFactoryLoader().createFactory(), systemName); } diff --git a/src/main/java/com/tacitknowledge/util/migration/jdbc/DatabaseType.java b/src/main/java/com/tacitknowledge/util/migration/jdbc/DatabaseType.java index 045ba27..d79f1bf 100644 --- a/src/main/java/com/tacitknowledge/util/migration/jdbc/DatabaseType.java +++ b/src/main/java/com/tacitknowledge/util/migration/jdbc/DatabaseType.java @@ -19,6 +19,9 @@ import java.io.InputStream; import java.util.Properties; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + /** * Defines a type of database (e.g. oracle or postgres. This * is used to help define the SQL that is used to update the patch table and as a hint @@ -51,6 +54,8 @@ */ public class DatabaseType { + private static Log log = LogFactory.getLog(DatabaseType.class); + /** * The SQL statements and properties that are unique to this database flavor. */ @@ -89,6 +94,16 @@ public DatabaseType(String databaseType) // this is okay, in this class, migration.properties is only used to override SQL } this.databaseType = databaseType; + log.debug("AutoPatch instantiated and loaded with properties per " + databasePropertiesFilename); + if (isMultipleStatementsSupported()) + { + log.debug("AutoPatch thinks that Multiple SQL Statements can be executed at once."); + } + else + { + log.debug("AutoPatch thinks that Multiple SQL Statements must be executed separately."); + } + } protected Properties loadProperties(String propertiesFilename, ClassLoader loader) diff --git a/src/main/java/com/tacitknowledge/util/migration/jdbc/JdbcMigrationLauncher.java b/src/main/java/com/tacitknowledge/util/migration/jdbc/JdbcMigrationLauncher.java index ed52e41..b6491a8 100644 --- a/src/main/java/com/tacitknowledge/util/migration/jdbc/JdbcMigrationLauncher.java +++ b/src/main/java/com/tacitknowledge/util/migration/jdbc/JdbcMigrationLauncher.java @@ -766,11 +766,13 @@ public void rollbackSuccessful(RollbackableMigrationTask task, int rollbackLevel public void setMigrationStrategy(String migrationStrategy) { + log.debug("Setting migration strategy to: "+migrationStrategy+ " (was: "+this.migrationStrategy+")"); this.migrationStrategy = migrationStrategy; } public String getMigrationStrategy() { + log.debug("Get-accessor says migration strategy is: "+migrationStrategy); return migrationStrategy; } } diff --git a/src/main/java/com/tacitknowledge/util/migration/jdbc/JdbcMigrationLauncherFactory.java b/src/main/java/com/tacitknowledge/util/migration/jdbc/JdbcMigrationLauncherFactory.java index b261b51..7a4204e 100644 --- a/src/main/java/com/tacitknowledge/util/migration/jdbc/JdbcMigrationLauncherFactory.java +++ b/src/main/java/com/tacitknowledge/util/migration/jdbc/JdbcMigrationLauncherFactory.java @@ -20,7 +20,7 @@ import com.tacitknowledge.util.migration.MigrationListener; import com.tacitknowledge.util.migration.jdbc.util.ConfigurationUtil; import com.tacitknowledge.util.migration.jdbc.util.NonPooledDataSource; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -123,6 +123,7 @@ public JdbcMigrationLauncher createMigrationLauncher(String systemName) public JdbcMigrationLauncher createMigrationLauncher(ServletContextEvent sce) throws MigrationException { + log.info("Creating JdbcMigrationLauncher for web-application."); JdbcMigrationLauncher launcher = getJdbcMigrationLauncher(); configureFromServletContext(launcher, sce); return launcher; @@ -139,6 +140,14 @@ public JdbcMigrationLauncher createMigrationLauncher(ServletContextEvent sce) private void configureFromServletContext(JdbcMigrationLauncher launcher, ServletContextEvent sce) throws MigrationException { + log.debug("Configuring launcher from Servlet Context (web.xml)"); + + String migrationStrategy = sce.getServletContext().getInitParameter("migration.strategy"); + log.debug("Servlet Container says migration.strategy = "+migrationStrategy); + if (!StringUtils.isBlank(migrationStrategy)) { + launcher.setMigrationStrategy(migrationStrategy); + } + String readOnly = sce.getServletContext().getInitParameter("migration.readonly"); launcher.setReadOnly(false); if ("true".equals(readOnly)) @@ -155,6 +164,7 @@ private void configureFromServletContext(JdbcMigrationLauncher launcher, } String patchPath = ConfigurationUtil.getRequiredParam("migration.patchpath", sce, this); + log.debug("migration.patchpath (required) = "+patchPath); launcher.setPatchPath(patchPath); String postPatchPath = sce.getServletContext().getInitParameter("migration.postpatchpath"); diff --git a/src/main/java/com/tacitknowledge/util/migration/jdbc/SqlScriptMigrationTask.java b/src/main/java/com/tacitknowledge/util/migration/jdbc/SqlScriptMigrationTask.java index e778235..6282cbc 100644 --- a/src/main/java/com/tacitknowledge/util/migration/jdbc/SqlScriptMigrationTask.java +++ b/src/main/java/com/tacitknowledge/util/migration/jdbc/SqlScriptMigrationTask.java @@ -20,7 +20,7 @@ import com.tacitknowledge.util.migration.MigrationTaskSupport; import com.tacitknowledge.util.migration.jdbc.util.SqlUtil; import com.tacitknowledge.util.migration.jdbc.util.SybaseUtil; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; diff --git a/src/main/java/com/tacitknowledge/util/migration/jdbc/StandaloneMigrationLauncher.java b/src/main/java/com/tacitknowledge/util/migration/jdbc/StandaloneMigrationLauncher.java index 2e20da2..87e65d3 100644 --- a/src/main/java/com/tacitknowledge/util/migration/jdbc/StandaloneMigrationLauncher.java +++ b/src/main/java/com/tacitknowledge/util/migration/jdbc/StandaloneMigrationLauncher.java @@ -19,7 +19,7 @@ import com.tacitknowledge.util.migration.MigrationException; import com.tacitknowledge.util.migration.jdbc.util.ConfigurationUtil; import com.tacitknowledge.util.migration.jdbc.util.MigrationUtil; -import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.picocontainer.PicoContainer; diff --git a/src/test/java/com/tacitknowledge/util/migration/DistributedMigrationProcessTest.java b/src/test/java/com/tacitknowledge/util/migration/DistributedMigrationProcessTest.java index 5b6cab6..a71f082 100644 --- a/src/test/java/com/tacitknowledge/util/migration/DistributedMigrationProcessTest.java +++ b/src/test/java/com/tacitknowledge/util/migration/DistributedMigrationProcessTest.java @@ -222,7 +222,7 @@ public void testDoRollbacksActionMockingAsIfOrderedStrategyWereUsed() throws Mig boolean forceRollback=false; int rollbacksApplied = distributedMigrationProcess.doRollbacks(patchInfoStoreMock,rollbackLevels,migrationContextMock,forceRollback); - assertEquals("Two rollbacks should be applied", 3, rollbacksApplied); + assertEquals("Rollbacks should have been applied", 3, rollbacksApplied); } } diff --git a/src/test/java/com/tacitknowledge/util/migration/MigrationRunnerFactoryTest.java b/src/test/java/com/tacitknowledge/util/migration/MigrationRunnerFactoryTest.java new file mode 100644 index 0000000..c3ce459 --- /dev/null +++ b/src/test/java/com/tacitknowledge/util/migration/MigrationRunnerFactoryTest.java @@ -0,0 +1,40 @@ +package com.tacitknowledge.util.migration; + +import junit.framework.TestCase; + +public class MigrationRunnerFactoryTest extends TestCase +{ + + protected void setUp() throws Exception + { + super.setUp(); + } + + public void testGetMigrationRunnerStrategy_Default() + { + MigrationRunnerStrategy strategy = MigrationRunnerFactory.getMigrationRunnerStrategy(""); + assertEquals("com.tacitknowledge.util.migration.OrderedMigrationRunnerStrategy",strategy.getClass().getName()); + } + + public void testGetMigrationRunnerStrategy_MissingPatchMigrationRunnerStrategy() + { + MigrationRunnerStrategy strategy = MigrationRunnerFactory.getMigrationRunnerStrategy("com.tacitknowledge.util.migration.MissingPatchMigrationRunnerStrategy"); + assertEquals("com.tacitknowledge.util.migration.MissingPatchMigrationRunnerStrategy",strategy.getClass().getName()); + } + + public void testGetMigrationRunnerStrategy_NoSuchStrategy() + { + try + { + MigrationRunnerFactory.getMigrationRunnerStrategy("com.tacitknowledge.util.migration.NoSuchStrategy"); + fail("MigrationRunnerFactory.getMigrationRunnerStrategy() Should have raised an IllegalArgumentException"); + } + catch (IllegalArgumentException e) + { + // expected + } + + } + + +} diff --git a/src/test/java/com/tacitknowledge/util/migration/jdbc/DistributedJdbcMigrationLauncherFactoryTest.java b/src/test/java/com/tacitknowledge/util/migration/jdbc/DistributedJdbcMigrationLauncherFactoryTest.java index 971f280..7864327 100644 --- a/src/test/java/com/tacitknowledge/util/migration/jdbc/DistributedJdbcMigrationLauncherFactoryTest.java +++ b/src/test/java/com/tacitknowledge/util/migration/jdbc/DistributedJdbcMigrationLauncherFactoryTest.java @@ -206,7 +206,7 @@ public void testDistributedReadOnlyMode() throws Exception log.debug("got exception: " + me.getMessage()); } - currentPatchLevel = 8; + currentPatchLevel = 13; // need to mock the patch info stores to return the expected patch levels setReportedPatchLevel(controlledSystems.values(), currentPatchLevel); diff --git a/src/test/java/com/tacitknowledge/util/migration/jdbc/SqlScriptMigrationTaskTest.java b/src/test/java/com/tacitknowledge/util/migration/jdbc/SqlScriptMigrationTaskTest.java index 9dc83b8..19264d7 100644 --- a/src/test/java/com/tacitknowledge/util/migration/jdbc/SqlScriptMigrationTaskTest.java +++ b/src/test/java/com/tacitknowledge/util/migration/jdbc/SqlScriptMigrationTaskTest.java @@ -20,8 +20,10 @@ import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; +import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.Properties; import javax.sql.DataSource; @@ -41,290 +43,354 @@ * * @author Scott Askew (scott@tacitknowledge.com) */ -public class SqlScriptMigrationTaskTest extends JDBCTestCaseAdapter -{ - private static Log log = LogFactory.getLog(SqlScriptMigrationTaskTest.class); - /** - * The task to test. - */ - private SqlScriptMigrationTask task = null; - - /** - * The JDBCMigrationConteext used for testing - */ - private DataSourceMigrationContext context = new DataSourceMigrationContext(); - - /** - * {@inheritDoc} - */ - protected void setUp() throws Exception - { - super.setUp(); - - MockConnection conn = getJDBCMockObjectFactory().getMockConnection(); - - context = new DataSourceMigrationContext(); - context.setDataSource(new ConnectionWrapperDataSource(conn)); - context.setSystemName("milestone"); - context.setDatabaseType(new DatabaseType("postgres")); - } - - /** - * Test doing a migration (with the connection silently succeeding) - * - * @throws IOException - * if the test patch file doesn't load correctly - */ - public void testMigrate() throws IOException - { - InputStream is = getClass().getResourceAsStream( - "test/patch0003_third_patch.sql"); - task = new SqlScriptMigrationTask("test", 1, is); - is.close(); - - try - { - task.migrate(context); - } catch (MigrationException me) - { - log.info("Unexpected exception", me); - fail("unexpected exception"); +public class SqlScriptMigrationTaskTest extends JDBCTestCaseAdapter { + private static Log log = LogFactory.getLog(SqlScriptMigrationTaskTest.class); + /** + * The task to test. + */ + private SqlScriptMigrationTask task = null; + + /** + * The JDBCMigrationConteext used for testing + */ + private DataSourceMigrationContext context = new DataSourceMigrationContext(); + + /** + * {@inheritDoc} + */ + protected void setUp() throws Exception { + super.setUp(); + + MockConnection conn = getJDBCMockObjectFactory().getMockConnection(); + + context = new DataSourceMigrationContext(); + context.setDataSource(new ConnectionWrapperDataSource(conn)); + context.setSystemName("milestone"); + context.setDatabaseType(new DatabaseType("postgres")); + } + + /** + * Test doing a migration (with the connection silently succeeding) + * + * @throws IOException + * if the test patch file doesn't load correctly + */ + public void testMigrate() throws IOException { + InputStream is = getClass().getResourceAsStream("test/patch0003_third_patch.sql"); + task = new SqlScriptMigrationTask("test", 1, is); + is.close(); + + try { + task.migrate(context); + } catch (MigrationException me) { + log.info("Unexpected exception", me); + fail("unexpected exception"); + } + } + + public void testLevel() throws Exception { + SqlScriptMigrationTaskSource source = new SqlScriptMigrationTaskSource(); + List tasks = null; + MigrationTaskSupport task = null; + try { + tasks = source.getMigrationTasks(this.getClass().getPackage().getName() + ".test"); + + task = findTaskByLevel(tasks, 1); + assertEquals("patch0001.sql", task.getName()); + + task = findTaskByLevel(tasks, 2); + assertEquals("patch0002_second_patch.sql", task.getName()); + + task = findTaskByLevel(tasks, 3); + assertEquals("patch0003_third_patch.sql", task.getName()); + } catch (Exception e) { + log.info("Unexpected exception", e); + fail(); + } + } + + private MigrationTaskSupport findTaskByLevel(List tasks, int level) { + for (Iterator iterator = tasks.iterator(); iterator.hasNext();) { + MigrationTaskSupport task = (MigrationTaskSupport) iterator.next(); + if (task.getLevel() == level) { + return task; + } + } + return null; + + } + + /** + * Ensures that a rollback script is correctly read from disk and then + * executed. + * + * @throws IOException + */ + public void testRollback() throws IOException { + SqlScriptMigrationTaskSource source = new SqlScriptMigrationTaskSource(); + List tasks = null; + try { + tasks = source.getMigrationTasks(this.getClass().getPackage().getName() + ".test"); + // patches 1 & 3 have rollbacks (2 & 4 do not) + MigrationTaskSupport task = findTaskByLevel(tasks, 3); + if (task.isRollbackSupported()) + task.down(context); + else + fail("Rollback should be supported for task " + task.getName()); + } catch (Exception e) { + log.info("Unexpected exception", e); + fail(); + } + } + + /** + * Ensures that isRollbackSupported returns false if there is no rollback + * script. + * + * @throws IOException + */ + public void testIsRollbackSupported() throws IOException { + SqlScriptMigrationTaskSource source = new SqlScriptMigrationTaskSource(); + List tasks = null; + int[] SQL_PATCHES_WITH_NO_ROLLBACK = { 2, 13 }; + + try { + tasks = source.getMigrationTasks(this.getClass().getPackage().getName() + ".test"); + + for (Iterator i = tasks.iterator(); i.hasNext();) { + RollbackableMigrationTask rollbackableTask = (RollbackableMigrationTask) i.next(); + if (Arrays.binarySearch(SQL_PATCHES_WITH_NO_ROLLBACK, rollbackableTask.getLevel()) > -1) + assertFalse(rollbackableTask.isRollbackSupported()); + else + assertTrue(rollbackableTask.isRollbackSupported()); + + } + + } catch (Exception e) { + log.info("Unexpected exception", e); + fail(); + } } - } - - /** - * Ensures that a rollback script is correctly read from disk and then executed. - * - * @throws IOException - */ - public void testRollback() throws IOException - { - SqlScriptMigrationTaskSource source = new SqlScriptMigrationTaskSource(); - List tasks = null; - try - { - tasks = source.getMigrationTasks(this.getClass().getPackage() - .getName() - + ".test"); - MigrationTaskSupport task = (MigrationTaskSupport)tasks.get(2); - - if(task.isRollbackSupported()) - task.down(context); - else - fail("Rollback should be supported for this task"); - } catch (Exception e) - { - log.info("Unexpected exception", e); - fail(); + + /** + * Ensures that the task can correctly parse multiple SQL statements from a + * single file, with embedded comments. + * + * @throws IOException + * if an unexpected error occurs while attempting to read the + * test SQL patch file; it's a system resource, so this + * shouldn't happen + */ + public void testParsingMultipleStatement_ProcessedSingly() throws IOException { + // patch0001.sql contains 3 statements, each with a closing semicolon + InputStream is = getClass().getResourceAsStream("test/patch0001.sql"); + task = new SqlScriptMigrationTask("test", 1, is); + is.close(); + + // For Oracle, the statements are processed separately + context.setDatabaseType(new DatabaseType("oracle")); + List statements = task.getSqlStatements(context); + assertEquals(3, statements.size()); + assertEquals("insert into user_role_assoc (user_role_id, application_user_id, " + + "role_code, project_id) \n\t\t\tvalues (nextval('role_id_seq'),2, 'SYSA', 3)", statements.get(0).toString()); + assertEquals("insert into user_role_assoc (user_role_id, application_user_id, " + + "role_code, project_id) \n\t\t\tvalues (nextval('role_id_seq'),3, 'SYSA', 3)", statements.get(1).toString()); + assertEquals("insert into user_role_assoc (user_role_id, application_user_id, " + + "role_code, project_id) \n\t\t\tvalues (nextval('role_--id_seq;'),4, 'SYSA', 3)", statements.get(2).toString()); + + } + + public void testParsingMultipleStatement_ProcessedTogether() throws IOException { + // patch0001.sql contains 3 statements, each with a closing semicolon + InputStream is = getClass().getResourceAsStream("test/patch0001.sql"); + task = new SqlScriptMigrationTask("test", 1, is); + is.close(); + + // For Postgres, the statements are processed together as one. (Same for + // MySQL, normally, but another test has a forced-override of the + // supportsMultipleStatements for MySQL, so we can't rely on it being + // correct here.) + String databaseType = "postgres"; + context.setDatabaseType(new DatabaseType(databaseType)); + + assertTrue(context.getDatabaseType().isMultipleStatementsSupported()); + List statementsl = task.getSqlStatements(context); + assertEquals(1, statementsl.size()); + assertEquals("\n insert into user_role_assoc (user_role_id, application_user_id, " + + "role_code, project_id) \n\t\t\tvalues (nextval('role_id_seq'),2, 'SYSA', 3); \n\n" + + "// Testing\n" + "insert into user_role_assoc (user_role_id, application_user_id, " + + "role_code, project_id) \n\t\t\tvalues (nextval('role_id_seq'),3, 'SYSA', 3);\n\n" + + " -- This is a comment\n" + "insert into user_role_assoc (user_role_id, application_user_id, " + + "role_code, project_id) \n\t\t\tvalues (nextval('role_--id_seq;'),4, 'SYSA', 3);\n \n" + " \n", statementsl + .get(0).toString()); + + } + + public void testParsingSingleStatement_NoSemicolon() throws IOException { + // patch0002_second_patch.sql contains a single statement with no + // closing semicolon + InputStream is = getClass().getResourceAsStream("test/patch0002_second_patch.sql"); + task = new SqlScriptMigrationTask("test", 1, is); + is.close(); + + context.setDatabaseType(new DatabaseType("oracle")); + List l = task.getSqlStatements(context); + assertEquals(1, l.size()); + assertEquals("insert into user_role_assoc (user_role_id, application_user_id, " + + "role_code, project_id) \n\t\t\tvalues (nextval('role_id_seq'),2, 'SYSA', 3)", l.get(0).toString()); + } + + /** + * Make sure that if we can do one big statement, it correctly does one big + * statement + * + * @exception IOException + * if an unexpected error happens while reading test SQL + */ + public void testParsingSingleStatement() throws IOException { + InputStream is = getClass().getResourceAsStream("test/patch0003_third_patch.sql"); + task = new SqlScriptMigrationTask("test", 1, is); + is.close(); + + List l = task.getSqlStatements(context); + assertEquals(1, l.size()); + assertEquals("select * from dual;\nselect * from dual;\n", l.get(0).toString()); } - } - - /** - * Ensures that isRollbackSupported returns false if there is no - * rollback script. - * - * @throws IOException - */ - public void testIsRollbackSupported() throws IOException - { - SqlScriptMigrationTaskSource source = new SqlScriptMigrationTaskSource(); - List tasks = null; - try - { - tasks = source.getMigrationTasks(this.getClass().getPackage() - .getName() - + ".test"); - - for(Iterator i=tasks.iterator(); i.hasNext();) { - //patch with ID 2 has no rollback - RollbackableMigrationTask rollbackableTask = (RollbackableMigrationTask) i.next(); - if(rollbackableTask.getLevel().equals(Integer.valueOf(2))) - assertFalse(rollbackableTask.isRollbackSupported()); - else - assertTrue(rollbackableTask.isRollbackSupported()); - - } - - } catch (Exception e) - { - log.info("Unexpected exception", e); - fail(); + + /** + * See that the name and toString are the same, given a file name to load + * + * @exception IOException + * if an unexpected error happens while reading test SQL + */ + public void testTaskName() throws IOException { + InputStream is = getClass().getResourceAsStream("test/patch0003_third_patch.sql"); + task = new SqlScriptMigrationTask("patch0003_third_patch", 1, is); + is.close(); + assertEquals("patch0003_third_patch", task.toString()); } - } - - /** - * Ensures that the task can correctly parse multiple SQL statements from a - * single file, with embedded comments. - * - * @throws IOException - * if an unexpected error occurs while attempting to read - * the test SQL patch file; it's a system resource, so this - * shouldn't happen - */ - public void testParsingMultipleStatement() throws IOException - { - InputStream is = getClass().getResourceAsStream( - "test/patch0001.sql"); - task = new SqlScriptMigrationTask("test", 1, is); - is.close(); - - context.setDatabaseType(new DatabaseType("oracle")); - List l = task.getSqlStatements(context); - assertEquals(3, l.size()); - assertEquals( - "insert into user_role_assoc (user_role_id, application_user_id, " - + "role_code, project_id) \n\t\t\tvalues (nextval('role_id_seq'),2, 'SYSA', 3)", - l.get(0).toString()); - assertEquals( - "insert into user_role_assoc (user_role_id, application_user_id, " - + "role_code, project_id) \n\t\t\tvalues (nextval('role_id_seq'),3, 'SYSA', 3)", - l.get(1).toString()); - assertEquals( - "insert into user_role_assoc (user_role_id, application_user_id, " - + "role_code, project_id) \n\t\t\tvalues (nextval('role_--id_seq;'),4, 'SYSA', 3)", - l.get(2).toString()); - - is = getClass().getResourceAsStream("test/patch0002_second_patch.sql"); - task = new SqlScriptMigrationTask("test", 1, is); - is.close(); - - l = task.getSqlStatements(context); - assertEquals(1, l.size()); - assertEquals( - "insert into user_role_assoc (user_role_id, application_user_id, " - + "role_code, project_id) \n\t\t\tvalues (nextval('role_id_seq'),2, 'SYSA', 3)", - l.get(0).toString()); - } - - /** - * Make sure that if we can do one big statement, it correctly does one big - * statement - * - * @exception IOException - * if an unexpected error happens while reading test SQL - */ - public void testParsingSingleStatement() throws IOException - { - InputStream is = getClass().getResourceAsStream( - "test/patch0003_third_patch.sql"); - task = new SqlScriptMigrationTask("test", 1, is); - is.close(); - - List l = task.getSqlStatements(context); - assertEquals(1, l.size()); - assertEquals("select * from dual;\nselect * from dual;\n", l.get(0) - .toString()); - } - - /** - * See that the name and toString are the same, given a file name to load - * - * @exception IOException - * if an unexpected error happens while reading test SQL - */ - public void testTaskName() throws IOException - { - InputStream is = getClass().getResourceAsStream( - "test/patch0003_third_patch.sql"); - task = new SqlScriptMigrationTask("patch0003_third_patch", 1, is); - is.close(); - assertEquals("patch0003_third_patch", task.toString()); - } - - /** - * Tests that sybase tsql statements are parsed correctly - * - * @throws IOException - * if an unexpected error occurs. - */ - public void testParsesSybaseTSql() throws IOException - { - InputStream is = getClass().getResourceAsStream("test/sybase_tsql.sql"); - assertNotNull(is); - task = new SqlScriptMigrationTask("sybase_tsql.sql", 1, is); - - MockDatabaseType dbType = new MockDatabaseType("sybase"); - dbType.setMultipleStatementsSupported(false); - context.setDatabaseType(dbType); - List statements = task.getSqlStatements(context); - assertEquals(8, statements.size()); - } - - /** - * Test that sybase database patches are committed when illegal multi - * statement transaction commands are used. - * - * @throws IOException - * if an unexpected error occurs - * @throws MigrationException - * if an unexpected error occurs - * @throws SQLException - * if an unexpected error occurs - */ - public void testSybasePatchesCommitsOnEveryStatement() throws IOException, - MigrationException, SQLException - { - InputStream is = getClass().getResourceAsStream("test/sybase_tsql.sql"); - assertNotNull(is); - task = new SqlScriptMigrationTask("sybase_tsql.sql", 1, is); - - MockDatabaseType dbType = new MockDatabaseType("sybase"); - dbType.setMultipleStatementsSupported(false); - context.setDatabaseType(dbType); - int numStatements = task.getSqlStatements(context).size(); - - // setup mocks to verify commits are called - MockControl dataSourceControl = MockControl - .createControl(DataSource.class); - DataSource dataSource = (DataSource) dataSourceControl.getMock(); - context.setDataSource(dataSource); - - MockControl connectionControl = MockControl - .createControl(Connection.class); - Connection connection = (Connection) connectionControl.getMock(); - - dataSourceControl.expectAndReturn(dataSource.getConnection(), - connection); - - MockControl statementControl = MockControl - .createControl(Statement.class); - Statement statement = (Statement) statementControl.getMock(); - statement.execute(""); - statementControl.setMatcher(MockControl.ALWAYS_MATCHER); - statementControl.setReturnValue(true, MockControl.ONE_OR_MORE); - statementControl.expectAndReturn(statement.isClosed(), false, MockControl.ONE_OR_MORE); - statement.close(); - statementControl.setVoidCallable(MockControl.ONE_OR_MORE); - - connectionControl.expectAndReturn(connection.isClosed(), false, - MockControl.ONE_OR_MORE); - connectionControl.expectAndReturn(connection.createStatement(), - statement, numStatements); - connectionControl.expectAndReturn(connection.getAutoCommit(), false, - MockControl.ONE_OR_MORE); - connection.commit(); - /* - * Magic Number 4 derived from the assumption that the fixture sql - * contains only one statement that is not allowed in a multi statement - * transaction: commit at beginning of migrate method commit prior to - * running the command not allowed in multi statement transaction to - * clear the transaction state. commit after running the multi statement - * transaction to clear transaction state for upcoming statements. - * commit at end of migrate method once all statements executed. + + /** + * Tests that sybase tsql statements are parsed correctly * - * Therefore, if you add more illegal statements to the fixture, add 2 - * more commit call's for each illegal statement. + * @throws IOException + * if an unexpected error occurs. */ - connectionControl.setVoidCallable(4); + public void testParsesSybaseTSql() throws IOException { + // sybase_tsql.sql contains multiple statements separated by "GO" + // keywords. GO must be on a line by itself, but it is case insensitive + // and leading and trailing whitespace is allowed. + InputStream is = getClass().getResourceAsStream("test/sybase_tsql.sql"); + assertNotNull(is); + task = new SqlScriptMigrationTask("sybase_tsql.sql", 1, is); + + MockDatabaseType dbType = new MockDatabaseType("sybase"); + dbType.setMultipleStatementsSupported(false); + context.setDatabaseType(dbType); + List statements = task.getSqlStatements(context); + assertEquals(8, statements.size()); + assertEquals("/* just some sane sql at first */\n" + " \n" + "\n" + "PRINT 'Creating photo table'", + statements.get(0).toString()); + assertEquals("/* will this table name screw up the parser looking for GO delimiter :)? */\n" + + "create table gogo\n" + "(\n" + "id numeric(14,0) NOT NULL\n" + "value varchar(32)\n" + ")", + statements.get(7).toString()); + } - dataSourceControl.replay(); - connectionControl.replay(); - statementControl.replay(); + /** + * Tests that multiple DDL statements are parsed correctly (when the + * MultipleStatementsSupported property is forced to FALSE). + * + * @throws IOException + * if an unexpected error occurs. + */ + public void testParsesDDL() throws IOException { + // some_lengthy_DDL_statements.sql contains multiple DDL statements that + // end with semicolons. + InputStream is = getClass().getResourceAsStream("test/some_lengthy_DDL_statements.sql"); + assertNotNull(is); + task = new SqlScriptMigrationTask("some_lengthy_DDL_statements.sql", 1, is); + + MockDatabaseType dbType = new MockDatabaseType("mysql"); + dbType.setMultipleStatementsSupported(false); + context.setDatabaseType(dbType); + List statements = task.getSqlStatements(context); + assertEquals(13, statements.size()); + assertEquals("CREATE TABLE `ref_country` ( \n" + " `isoCode` varchar(10) NOT NULL DEFAULT '', \n" + + " `perceivedCorruption` decimal(9,2) DEFAULT NULL, \n" + + " `currency` char(3) NOT NULL DEFAULT 'USD', \n" + " PRIMARY KEY (`isoCode`) \n" + + " ) ENGINE=InnoDB DEFAULT CHARSET=latin1", statements.get(0).toString()); + assertEquals("UPDATE provider_registration_request a\n" + + " JOIN ref_state rs on a.state = rs.isoCode and a.country = rs.countryCode\n" + + " SET countrySubdivision = concat(country,'-',state)\n" + " WHERE state is not null \n" + + " AND countrySubdivision is null", statements.get(12).toString()); + } - // run tests - task.migrate(context); - dataSourceControl.verify(); - connectionControl.verify(); - } + /** + * Test that sybase database patches are committed when illegal multi + * statement transaction commands are used. + * + * @throws IOException + * if an unexpected error occurs + * @throws MigrationException + * if an unexpected error occurs + * @throws SQLException + * if an unexpected error occurs + */ + public void testSybasePatchesCommitsOnEveryStatement() throws IOException, MigrationException, SQLException { + InputStream is = getClass().getResourceAsStream("test/sybase_tsql.sql"); + assertNotNull(is); + task = new SqlScriptMigrationTask("sybase_tsql.sql", 1, is); + + MockDatabaseType dbType = new MockDatabaseType("sybase"); + dbType.setMultipleStatementsSupported(false); + context.setDatabaseType(dbType); + int numStatements = task.getSqlStatements(context).size(); + + // setup mocks to verify commits are called + MockControl dataSourceControl = MockControl.createControl(DataSource.class); + DataSource dataSource = (DataSource) dataSourceControl.getMock(); + context.setDataSource(dataSource); + + MockControl connectionControl = MockControl.createControl(Connection.class); + Connection connection = (Connection) connectionControl.getMock(); + + dataSourceControl.expectAndReturn(dataSource.getConnection(), connection); + + MockControl statementControl = MockControl.createControl(Statement.class); + Statement statement = (Statement) statementControl.getMock(); + statement.execute(""); + statementControl.setMatcher(MockControl.ALWAYS_MATCHER); + statementControl.setReturnValue(true, MockControl.ONE_OR_MORE); + statementControl.expectAndReturn(statement.isClosed(), false, MockControl.ONE_OR_MORE); + statement.close(); + statementControl.setVoidCallable(MockControl.ONE_OR_MORE); + + connectionControl.expectAndReturn(connection.isClosed(), false, MockControl.ONE_OR_MORE); + connectionControl.expectAndReturn(connection.createStatement(), statement, numStatements); + connectionControl.expectAndReturn(connection.getAutoCommit(), false, MockControl.ONE_OR_MORE); + connection.commit(); + /* + * Magic Number 4 derived from the assumption that the fixture sql + * contains only one statement that is not allowed in a multi statement + * transaction: commit at beginning of migrate method commit prior to + * running the command not allowed in multi statement transaction to + * clear the transaction state. commit after running the multi statement + * transaction to clear transaction state for upcoming statements. + * commit at end of migrate method once all statements executed. + * + * Therefore, if you add more illegal statements to the fixture, add 2 + * more commit call's for each illegal statement. + */ + connectionControl.setVoidCallable(4); + + dataSourceControl.replay(); + connectionControl.replay(); + statementControl.replay(); + + // run tests + task.migrate(context); + dataSourceControl.verify(); + connectionControl.verify(); + } } diff --git a/src/test/java/com/tacitknowledge/util/migration/tasks/normal/TestMigrationTask2.java b/src/test/java/com/tacitknowledge/util/migration/tasks/normal/TestMigrationTask2.java index 1d83808..ab4cfc6 100644 --- a/src/test/java/com/tacitknowledge/util/migration/tasks/normal/TestMigrationTask2.java +++ b/src/test/java/com/tacitknowledge/util/migration/tasks/normal/TestMigrationTask2.java @@ -30,7 +30,7 @@ public class TestMigrationTask2 extends BaseTestMigrationTask private static Integer patchLevelOverride = new Integer(5); /** - * Creates a new TestMigrationTask3. + * Creates a new TestMigrationTask2. */ public TestMigrationTask2() { diff --git a/src/test/resources/com/tacitknowledge/util/migration/jdbc/test/some_lengthy_DDL_statements.sql b/src/test/resources/com/tacitknowledge/util/migration/jdbc/test/some_lengthy_DDL_statements.sql new file mode 100644 index 0000000..59fc8b6 --- /dev/null +++ b/src/test/resources/com/tacitknowledge/util/migration/jdbc/test/some_lengthy_DDL_statements.sql @@ -0,0 +1,208 @@ +CREATE TABLE `ref_country` ( + `isoCode` varchar(10) NOT NULL DEFAULT '', + `perceivedCorruption` decimal(9,2) DEFAULT NULL, + `currency` char(3) NOT NULL DEFAULT 'USD', + PRIMARY KEY (`isoCode`) + ) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +CREATE TABLE `ref_country_subdivision` ( + `isoCode` varchar(10) NOT NULL, + `countryCode` varchar(10) NOT NULL, + PRIMARY KEY (`isoCode`,`countryCode`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `ref_state` ( + `isoCode` char(10) NOT NULL, + `countryCode` char(2) NOT NULL, + PRIMARY KEY (`isoCode`,`countryCode`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `accounts` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL, + `createdBy` int(11) DEFAULT NULL, + `creationDate` datetime DEFAULT NULL, + `updatedBy` int(11) DEFAULT NULL, + `updateDate` datetime DEFAULT NULL, + `status` varchar(15) NOT NULL DEFAULT 'Pending', + `address` varchar(50) DEFAULT NULL, + `address2` varchar(50) DEFAULT NULL, + `address3` varchar(50) DEFAULT NULL, + `city` varchar(35) DEFAULT NULL, + `state` varchar(10) DEFAULT NULL, + `zip` varchar(15) DEFAULT NULL, + `country` varchar(25) DEFAULT NULL, + `phone` varchar(30) DEFAULT NULL, + `phone2` varchar(35) DEFAULT NULL, + `fax` varchar(30) DEFAULT NULL, + `contactID` mediumint(9) DEFAULT NULL, + `email` varchar(50) DEFAULT NULL, + `web_URL` varchar(50) DEFAULT NULL, + `nameIndex` varchar(50) DEFAULT NULL, + `reason` varchar(100) DEFAULT NULL, + `description` text, + `needsIndexing` tinyint(4) unsigned NOT NULL DEFAULT '1', + `parentID` int(11) DEFAULT NULL, + `currencyCode` char(3) DEFAULT 'USD', + `locale` varchar(5) DEFAULT 'en', + `timezone` varchar(50) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `name` (`name`), + KEY `nameIndex` (`nameIndex`) + ) ENGINE=InnoDB AUTO_INCREMENT=33517 DEFAULT CHARSET=latin1; + +CREATE TABLE `provider_audit` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `creationDate` datetime DEFAULT NULL, + `createdBy` int(11) DEFAULT NULL, + `updateDate` datetime DEFAULT NULL, + `updatedBy` int(11) DEFAULT NULL, + `expiresDate` datetime DEFAULT NULL, + `effectiveDate` datetime DEFAULT NULL, + `assignedDate` datetime DEFAULT NULL, + `scheduledDate` datetime DEFAULT NULL, + `lastRecalculation` date DEFAULT NULL, + `score` tinyint(3) DEFAULT '0', + `contact` varchar(50) DEFAULT NULL, + `phone` varchar(25) DEFAULT NULL, + `phone2` varchar(255) DEFAULT NULL, + `address` varchar(50) DEFAULT NULL, + `address2` varchar(50) DEFAULT NULL, + `city` varchar(35) DEFAULT NULL, + `state` varchar(10) DEFAULT NULL, + `zip` varchar(10) DEFAULT NULL, + `country` varchar(50) DEFAULT NULL, + `latitude` float NOT NULL DEFAULT '0', + `longitude` float NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) + ) ENGINE=InnoDB AUTO_INCREMENT=761716 DEFAULT CHARSET=latin1 + +CREATE TABLE `provider_info` ( + `id` int(11) NOT NULL, + `description` text, + `secondContact` varchar(50) DEFAULT NULL, + `secondPhone` varchar(50) DEFAULT NULL, + `secondEmail` varchar(50) DEFAULT NULL, + `billingContact` varchar(50) DEFAULT NULL, + `billingPhone` varchar(50) DEFAULT NULL, + `billingEmail` varchar(50) DEFAULT NULL, + `billingAddress` varchar(50) DEFAULT NULL, + `billingCity` varchar(35) DEFAULT NULL, + `billingState` varchar(10) DEFAULT NULL, + `billingCountrySubdivision` varchar(10) DEFAULT NULL, + `billingZip` varchar(10) DEFAULT NULL, + `billingCountry` varchar(25) DEFAULT NULL, + `membershipDate` date DEFAULT NULL, + `paymentMethod` varchar(20) DEFAULT 'CreditCard', + `paymentMethodStatus` varchar(20) DEFAULT NULL, + `renew` tinyint(4) DEFAULT '1', + `lastUpgradeDate` date DEFAULT NULL, + `balance` decimal(9,2) DEFAULT '0.00', + `needsRecalculation` tinyint(4) NOT NULL DEFAULT '1', + `lastRecalculation` datetime DEFAULT NULL, + `ccOnFile` tinyint(4) NOT NULL DEFAULT '0', + `ccExpiration` date DEFAULT NULL, + `ccEmail` varchar(50) DEFAULT NULL, + `showInDirectory` tinyint(4) DEFAULT '1', + PRIMARY KEY (`id`), + CONSTRAINT `FK_contractor_info` FOREIGN KEY (`id`) REFERENCES `accounts` (`id`) ON DELETE CASCADE + ) ENGINE=InnoDB DEFAULT CHARSET=latin1 + +CREATE TABLE `provider_registration_request` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(100) NOT NULL, + `createdBy` int(11) DEFAULT NULL, + `updatedBy` int(11) DEFAULT NULL, + `creationDate` datetime DEFAULT NULL, + `updateDate` datetime DEFAULT NULL, + `requestedByID` mediumint(9) NOT NULL, + `requestedByUser` varchar(20) DEFAULT NULL, + `status` varchar(30) NOT NULL DEFAULT 'Active', + `contact` varchar(30) NOT NULL, + `phone` varchar(20) DEFAULT NULL, + `email` varchar(50) DEFAULT NULL, + `address` varchar(100) DEFAULT NULL, + `city` varchar(50) DEFAULT NULL, + `state` varchar(10) DEFAULT NULL, + `zip` varchar(10) DEFAULT NULL, + `country` char(2) DEFAULT NULL, + `deadline` date DEFAULT NULL, + `lastContactedBy` mediumint(9) DEFAULT NULL, + `lastContactDate` datetime DEFAULT NULL, + `contactCountByEmail` tinyint(4) unsigned NOT NULL DEFAULT '0', + `contactCountByPhone` tinyint(4) unsigned NOT NULL DEFAULT '0', + `notes` text, + `holdDate` date DEFAULT NULL, + `reasonForRegistration` varchar(500) DEFAULT NULL, + `reasonForDecline` varchar(500) DEFAULT NULL, + `closedOnDate` date DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `NameRequestedByIDUnique` (`name`,`requestedByID`), + KEY `status` (`status`,`country`,`state`) + ) ENGINE=InnoDB AUTO_INCREMENT=7465 DEFAULT CHARSET=latin1 + + + +ALTER TABLE `accounts` + ADD COLUMN `countrySubdivision` varchar(10) COLLATE latin1_swedish_ci NULL after `state`, + CHANGE `zip` `zip` varchar(15) COLLATE latin1_swedish_ci NULL after `countrySubdivision`, + CHANGE `country` `country` varchar(25) COLLATE latin1_swedish_ci NULL after `zip`, + CHANGE `phone` `phone` varchar(30) COLLATE latin1_swedish_ci NULL after `country`, + CHANGE `phone2` `phone2` varchar(35) COLLATE latin1_swedish_ci NULL after `phone`, + CHANGE `fax` `fax` varchar(30) COLLATE latin1_swedish_ci NULL after `phone2`, + CHANGE `contactID` `contactID` mediumint(9) NULL after `fax`, + CHANGE `email` `email` varchar(50) COLLATE latin1_swedish_ci NULL after `contactID`, + CHANGE `web_URL` `web_URL` varchar(50) COLLATE latin1_swedish_ci NULL after `email`, + CHANGE `nameIndex` `nameIndex` varchar(50) COLLATE latin1_swedish_ci NULL after `web_URL`, + CHANGE `reason` `reason` varchar(100) COLLATE latin1_swedish_ci NULL after `nameIndex`, + CHANGE `description` `description` text COLLATE latin1_swedish_ci NULL after `reason`, + CHANGE `needsIndexing` `needsIndexing` tinyint(4) unsigned NOT NULL DEFAULT '1' after `description`, + CHANGE `parentID` `parentID` int(11) NULL after `needsIndexing`, + CHANGE `currencyCode` `currencyCode` char(3) COLLATE latin1_swedish_ci NULL DEFAULT 'USD' after `parentID`, + CHANGE `locale` `locale` varchar(5) COLLATE latin1_swedish_ci NULL DEFAULT 'en' after `currencyCode`, + CHANGE `timezone` `timezone` varchar(50) COLLATE latin1_swedish_ci NULL after `locale`, COMMENT=''; + +ALTER TABLE `provider_audit` + CHANGE `state` `state` varchar(10) COLLATE latin1_swedish_ci NULL after `city`, + ADD COLUMN `countrySubdivision` varchar(10) COLLATE latin1_swedish_ci NULL after `state`, + CHANGE `zip` `zip` varchar(10) COLLATE latin1_swedish_ci NULL after `countrySubdivision`, + CHANGE `country` `country` varchar(50) COLLATE latin1_swedish_ci NULL after `zip`, + CHANGE `latitude` `latitude` float NOT NULL DEFAULT '0' after `country`, + CHANGE `longitude` `longitude` float NOT NULL DEFAULT '0' after `latitude`; + +ALTER TABLE `provider_info` + ADD COLUMN `billingCountrySubdivision` varchar(10) COLLATE latin1_swedish_ci NULL after `billingState`; + +ALTER TABLE `provider_registration_request` + ADD COLUMN `countrySubdivision` varchar(10) COLLATE latin1_swedish_ci NULL after `state`; + +ALTER TABLE `provider_info` + ADD CONSTRAINT `FK_provider_info` + FOREIGN KEY (`id`) REFERENCES `accounts` (`id`) ON DELETE CASCADE; + + + +UPDATE accounts a + JOIN ref_state rs on a.state = rs.isoCode and a.country = rs.countryCode + SET countrySubdivision = concat(country,'-',state) + WHERE state is not null + AND countrySubdivision is null; + +UPDATE provider_audit a + JOIN ref_state rs on a.state = rs.isoCode and a.country = rs.countryCode + SET countrySubdivision = concat(country,'-',state) + WHERE a.state is not null + AND a.countrySubdivision is null; + +UPDATE provider_info a + JOIN ref_state rs on a.billingState = rs.isoCode and a.billingCountry = rs.countryCode + SET billingCountrySubdivision = concat(billingCountry,'-',billingState) + WHERE a.billingState is not null + AND a.billingCountrySubdivision is null; + +UPDATE provider_registration_request a + JOIN ref_state rs on a.state = rs.isoCode and a.country = rs.countryCode + SET countrySubdivision = concat(country,'-',state) + WHERE state is not null + AND countrySubdivision is null; +