diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseFlywayMigrateDatabaseCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseFlywayMigrateDatabaseCommand.java index db2da8d25c5..06e99e469f2 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseFlywayMigrateDatabaseCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseFlywayMigrateDatabaseCommand.java @@ -20,8 +20,11 @@ package ca.uhn.fhir.cli; * #L% */ +import ca.uhn.fhir.jpa.migrate.BaseMigrator; +import ca.uhn.fhir.jpa.migrate.BruteForceMigrator; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; import ca.uhn.fhir.jpa.migrate.FlywayMigrator; +import ca.uhn.fhir.jpa.migrate.SchemaMigrator; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; @@ -35,10 +38,14 @@ import java.util.stream.Collectors; import static org.apache.commons.lang3.StringUtils.defaultString; +/** + * NB since 2019-12-05: This class is kind of weirdly named now, since it can either use Flyway or not use Flyway + */ public abstract class BaseFlywayMigrateDatabaseCommand extends BaseCommand { public static final String MIGRATE_DATABASE = "migrate-database"; + public static final String DONT_USE_FLYWAY = "dont-use-flyway"; private Set myFlags; private String myMigrationTableName; @@ -78,6 +85,7 @@ public abstract class BaseFlywayMigrateDatabaseCommand extends B addRequiredOption(retVal, "p", "password", "Password", "The JDBC database password"); addRequiredOption(retVal, "d", "driver", "Driver", "The database driver to use (Options are " + driverOptions() + ")"); addOptionalOption(retVal, "x", "flags", "Flags", "A comma-separated list of any specific migration flags (these flags are version specific, see migrator documentation for details)"); + addOptionalOption(retVal, null, DONT_USE_FLYWAY,false, "If this option is set, the migrator will not use FlywayDB for migration. This setting should only be used if you are trying to migrate a legacy database platform that is not supported by FlywayDB."); addOptionalOption(retVal, null, "no-column-shrink", false, "If this flag is set, the system will not attempt to reduce the length of columns. This is useful in environments with a lot of existing data, where shrinking a column can take a very long time."); return retVal; @@ -110,7 +118,14 @@ public abstract class BaseFlywayMigrateDatabaseCommand extends B .filter(StringUtils::isNotBlank) .collect(Collectors.toSet()); - FlywayMigrator migrator = new FlywayMigrator(myMigrationTableName); + boolean dontUseFlyway = theCommandLine.hasOption("dont-use-flyway"); + + BaseMigrator migrator; + if (dontUseFlyway) { + migrator = new BruteForceMigrator(); + } else { + migrator = new FlywayMigrator(myMigrationTableName); + } migrator.setConnectionUrl(url); migrator.setDriverType(driverType); migrator.setUsername(username); @@ -121,7 +136,7 @@ public abstract class BaseFlywayMigrateDatabaseCommand extends B migrator.migrate(); } - protected abstract void addTasks(FlywayMigrator theMigrator); + protected abstract void addTasks(BaseMigrator theMigrator); public void setMigrationTableName(String theMigrationTableName) { myMigrationTableName = theMigrationTableName; diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommand.java index 33abbbb7b24..19e1b52e746 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommand.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.cli; * #L% */ +import ca.uhn.fhir.jpa.migrate.BaseMigrator; import ca.uhn.fhir.jpa.migrate.FlywayMigrator; import ca.uhn.fhir.jpa.migrate.SchemaMigrator; import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask; @@ -44,7 +45,7 @@ public class HapiFlywayMigrateDatabaseCommand extends BaseFlywayMigrateDatabaseC } @Override - protected void addTasks(FlywayMigrator theMigrator) { + protected void addTasks(BaseMigrator theMigrator) { List> tasks = new HapiFhirJpaMigrationTasks(getFlags()).getAllTasks(VersionEnum.values()); theMigrator.addTasks(tasks); } diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java index 5aa3777d3f4..cdb552f032b 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java @@ -77,6 +77,47 @@ public class HapiFlywayMigrateDatabaseCommandTest { }); } + @Test + public void testMigrateFrom340_NoFlyway() throws IOException, SQLException { + + File location = getLocation("migrator_h2_test_340_current"); + + String url = "jdbc:h2:" + location.getAbsolutePath() + ";create=true"; + DriverTypeEnum.ConnectionProperties connectionProperties = DriverTypeEnum.H2_EMBEDDED.newConnectionProperties(url, "", ""); + + String initSql = "/persistence_create_h2_340.sql"; + executeSqlStatements(connectionProperties, initSql); + + seedDatabase340(connectionProperties); + + ourLog.info("**********************************************"); + ourLog.info("Done Setup, Starting Migration..."); + ourLog.info("**********************************************"); + + String[] args = new String[]{ + BaseFlywayMigrateDatabaseCommand.MIGRATE_DATABASE, + "-d", "H2_EMBEDDED", + "-u", url, + "-n", "", + "-p", "", + "--" + BaseFlywayMigrateDatabaseCommand.DONT_USE_FLYWAY + }; + assertFalse(JdbcUtils.getTableNames(connectionProperties).contains("HFJ_RES_REINDEX_JOB")); + App.main(args); + assertTrue(JdbcUtils.getTableNames(connectionProperties).contains("HFJ_RES_REINDEX_JOB")); + + connectionProperties.getTxTemplate().execute(t -> { + JdbcTemplate jdbcTemplate = connectionProperties.newJdbcTemplate(); + List> values = jdbcTemplate.queryForList("SELECT * FROM hfj_spidx_token"); + assertEquals(1, values.size()); + assertEquals("identifier", values.get(0).get("SP_NAME")); + assertEquals("12345678", values.get(0).get("SP_VALUE")); + assertTrue(values.get(0).keySet().contains("HASH_IDENTITY")); + assertEquals(7001889285610424179L, values.get(0).get("HASH_IDENTITY")); + return null; + }); + } + @Test public void testMigrateFromEmptySchema() throws IOException, SQLException { diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/BaseMigrator.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/BaseMigrator.java new file mode 100644 index 00000000000..4d7fb50aa40 --- /dev/null +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/BaseMigrator.java @@ -0,0 +1,72 @@ +package ca.uhn.fhir.jpa.migrate; + +import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask; +import org.flywaydb.core.api.MigrationInfoService; + +import java.util.List; +import java.util.Optional; + +public abstract class BaseMigrator { + + private boolean myDryRun; + private boolean myNoColumnShrink; + private DriverTypeEnum myDriverType; + private String myConnectionUrl; + private String myUsername; + private String myPassword; + + public abstract void migrate(); + + public boolean isDryRun() { + return myDryRun; + } + + public void setDryRun(boolean theDryRun) { + myDryRun = theDryRun; + } + + public boolean isNoColumnShrink() { + return myNoColumnShrink; + } + + public void setNoColumnShrink(boolean theNoColumnShrink) { + myNoColumnShrink = theNoColumnShrink; + } + + public abstract Optional getMigrationInfo(); + + public abstract void addTasks(List> theMigrationTasks); + + public DriverTypeEnum getDriverType() { + return myDriverType; + } + + public void setDriverType(DriverTypeEnum theDriverType) { + myDriverType = theDriverType; + } + + public String getConnectionUrl() { + return myConnectionUrl; + } + + public void setConnectionUrl(String theConnectionUrl) { + myConnectionUrl = theConnectionUrl; + } + + public String getUsername() { + return myUsername; + } + + public void setUsername(String theUsername) { + myUsername = theUsername; + } + + public String getPassword() { + return myPassword; + } + + public void setPassword(String thePassword) { + myPassword = thePassword; + } + +} diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/BruteForceMigrator.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/BruteForceMigrator.java new file mode 100644 index 00000000000..6853aafd13c --- /dev/null +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/BruteForceMigrator.java @@ -0,0 +1,50 @@ +package ca.uhn.fhir.jpa.migrate; + +import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import org.flywaydb.core.api.MigrationInfoService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * This class is an alternative to {@link FlywayMigrator). It doesn't use Flyway, but instead just + * executes all tasks. + */ +public class BruteForceMigrator extends BaseMigrator { + + private static final Logger ourLog = LoggerFactory.getLogger(BruteForceMigrator.class); + private List> myTasks = new ArrayList<>(); + + @Override + public void migrate() { + for (BaseTask next : myTasks) { + next.setDriverType(getDriverType()); + next.setDryRun(isDryRun()); + next.setNoColumnShrink(isNoColumnShrink()); + DriverTypeEnum.ConnectionProperties connectionProperties = getDriverType().newConnectionProperties(getConnectionUrl(), getUsername(), getPassword()); + next.setConnectionProperties(connectionProperties); + + try { + ourLog.info("Executing task of type: {}", next.getClass().getSimpleName()); + next.execute(); + } catch (SQLException e) { + throw new InternalErrorException(e); + } + } + } + + @Override + public Optional getMigrationInfo() { + return Optional.empty(); + } + + @Override + public void addTasks(List> theMigrationTasks) { + myTasks.addAll(theMigrationTasks); + } +} diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/FlywayMigrator.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/FlywayMigrator.java index a10981d50be..598e4108128 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/FlywayMigrator.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/FlywayMigrator.java @@ -31,34 +31,30 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; +import java.util.Optional; -public class FlywayMigrator { +public class FlywayMigrator extends BaseMigrator { private static final Logger ourLog = LoggerFactory.getLogger(FlywayMigrator.class); - private DriverTypeEnum myDriverType; private final String myMigrationTableName; - private String myConnectionUrl; - private String myUsername; - private String myPassword; private List myTasks = new ArrayList<>(); - private boolean myDryRun; - private boolean myNoColumnShrink; public FlywayMigrator(String theMigrationTableName, BasicDataSource theDataSource) { this(theMigrationTableName); - myConnectionUrl = theDataSource.getUrl(); - myUsername = theDataSource.getUsername(); - myPassword = theDataSource.getPassword(); + setConnectionUrl(theDataSource.getUrl()); + setUsername(theDataSource.getUsername()); + setPassword(theDataSource.getPassword()); String driverClassName = theDataSource.getDriverClassName(); if (driverClassName == null) { ourLog.error(this.getClass().getSimpleName() + " constructed without a database driver"); } else { - myDriverType = DriverTypeEnum.fromDriverClassName(driverClassName); - if (myDriverType == null) { + DriverTypeEnum driverType = DriverTypeEnum.fromDriverClassName(driverClassName); + if (driverType == null) { ourLog.error("Unknown driver class " + driverClassName); } + setDriverType(driverType); } } @@ -66,32 +62,13 @@ public class FlywayMigrator { myMigrationTableName = theMigrationTableName; } - public void setDriverType(DriverTypeEnum theDriverType) { - myDriverType = theDriverType; - } - - public void setConnectionUrl(String theConnectionUrl) { - myConnectionUrl = theConnectionUrl; - } - - public void setUsername(String theUsername) { - myUsername = theUsername; - } - - public void setPassword(String thePassword) { - myPassword = thePassword; - } - public void addTask(BaseTask theTask) { myTasks.add(new FlywayMigration(theTask, this)); } - public void setDryRun(boolean theDryRun) { - myDryRun = theDryRun; - } - + @Override public void migrate() { - try (DriverTypeEnum.ConnectionProperties connectionProperties = myDriverType.newConnectionProperties(myConnectionUrl, myUsername, myPassword)) { + try (DriverTypeEnum.ConnectionProperties connectionProperties = getDriverType().newConnectionProperties(getConnectionUrl(), getUsername(), getPassword())) { Flyway flyway = initFlyway(connectionProperties); flyway.migrate(); } catch (Exception e) { @@ -103,7 +80,7 @@ public class FlywayMigrator { // TODO KHS Is there a way we can use datasource instead of url, username, password here Flyway flyway = Flyway.configure() .table(myMigrationTableName) - .dataSource(myConnectionUrl, myUsername, myPassword) + .dataSource(getConnectionUrl(), getUsername(), getPassword()) .baselineOnMigrate(true) .javaMigrations(myTasks.toArray(new JavaMigration[0])) .load(); @@ -113,45 +90,19 @@ public class FlywayMigrator { return flyway; } + @Override public void addTasks(List> theTasks) { theTasks.forEach(this::addTask); } - public void setNoColumnShrink(boolean theNoColumnShrink) { - myNoColumnShrink = theNoColumnShrink; - } - - public DriverTypeEnum getDriverType() { - return myDriverType; - } - - public String getConnectionUrl() { - return myConnectionUrl; - } - - public String getUsername() { - return myUsername; - } - - public String getPassword() { - return myPassword; - } - - public boolean isDryRun() { - return myDryRun; - } - - public boolean isNoColumnShrink() { - return myNoColumnShrink; - } - - public MigrationInfoService getMigrationInfo() { - if (myDriverType == null) { - return null; + @Override + public Optional getMigrationInfo() { + if (getDriverType() == null) { + return Optional.empty(); } - try (DriverTypeEnum.ConnectionProperties connectionProperties = myDriverType.newConnectionProperties(myConnectionUrl, myUsername, myPassword)) { + try (DriverTypeEnum.ConnectionProperties connectionProperties = getDriverType().newConnectionProperties(getConnectionUrl(), getUsername(), getPassword())) { Flyway flyway = initFlyway(connectionProperties); - return flyway.info(); + return Optional.of(flyway.info()); } } diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/SchemaMigrator.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/SchemaMigrator.java index da7cf51105e..deab31c0661 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/SchemaMigrator.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/SchemaMigrator.java @@ -32,25 +32,36 @@ import org.slf4j.LoggerFactory; import java.sql.Connection; import java.sql.SQLException; import java.util.List; +import java.util.Optional; import java.util.Properties; public class SchemaMigrator { - private static final Logger ourLog = LoggerFactory.getLogger(SchemaMigrator.class); public static final String HAPI_FHIR_MIGRATION_TABLENAME = "FLY_HFJ_MIGRATION"; - + private static final Logger ourLog = LoggerFactory.getLogger(SchemaMigrator.class); private final BasicDataSource myDataSource; - private final FlywayMigrator myMigrator; private final boolean mySkipValidation; + private final String myMigrationTableName; + private final List> myMigrationTasks; + private boolean myDontUseFlyway; + private DriverTypeEnum myDriverType; + /** + * Constructor + */ public SchemaMigrator(String theMigrationTableName, BasicDataSource theDataSource, Properties jpaProperties, List> theMigrationTasks) { myDataSource = theDataSource; + myMigrationTableName = theMigrationTableName; + myMigrationTasks = theMigrationTasks; + if (jpaProperties.containsKey(AvailableSettings.HBM2DDL_AUTO) && "update".equals(jpaProperties.getProperty(AvailableSettings.HBM2DDL_AUTO))) { mySkipValidation = true; } else { mySkipValidation = false; } - myMigrator = new FlywayMigrator(theMigrationTableName, theDataSource); - myMigrator.addTasks(theMigrationTasks); + } + + public void setDontUseFlyway(boolean theDontUseFlyway) { + myDontUseFlyway = theDontUseFlyway; } public void validate() { @@ -59,13 +70,15 @@ public class SchemaMigrator { return; } try (Connection connection = myDataSource.getConnection()) { - MigrationInfoService migrationInfo = myMigrator.getMigrationInfo(); - if (migrationInfo.pending().length > 0) { - throw new ConfigurationException("The database schema for " + myDataSource.getUrl() + " is out of date. " + - "Current database schema version is " + getCurrentVersion(migrationInfo) + ". Schema version required by application is " + - getLastVersion(migrationInfo) + ". Please run the database migrator."); + Optional migrationInfo = newMigrator().getMigrationInfo(); + if (migrationInfo.isPresent()) { + if (migrationInfo.get().pending().length > 0) { + throw new ConfigurationException("The database schema for " + myDataSource.getUrl() + " is out of date. " + + "Current database schema version is " + getCurrentVersion(migrationInfo.get()) + ". Schema version required by application is " + + getLastVersion(migrationInfo.get()) + ". Please run the database migrator."); + } + ourLog.info("Database schema confirmed at expected version " + getCurrentVersion(migrationInfo.get())); } - ourLog.info("Database schema confirmed at expected version " + getCurrentVersion(migrationInfo)); } catch (SQLException e) { throw new ConfigurationException("Unable to connect to " + myDataSource.toString(), e); } @@ -76,7 +89,22 @@ public class SchemaMigrator { ourLog.warn("Database running in hibernate auto-update mode. Skipping schema migration."); return; } - myMigrator.migrate(); + newMigrator().migrate(); + } + + private BaseMigrator newMigrator() { + BaseMigrator migrator; + if (myDontUseFlyway) { + migrator = new BruteForceMigrator(); + migrator.setDriverType(myDriverType); + migrator.setConnectionUrl(myDataSource.getUrl()); + migrator.setUsername(myDataSource.getUsername()); + migrator.setPassword(myDataSource.getPassword()); + } else { + migrator = new FlywayMigrator(myMigrationTableName, myDataSource); + } + migrator.addTasks(myMigrationTasks); + return migrator; } private String getCurrentVersion(MigrationInfoService theMigrationInfo) { @@ -94,4 +122,8 @@ public class SchemaMigrator { } return "unknown"; } + + public void setDriverType(DriverTypeEnum theDriverType) { + myDriverType = theDriverType; + } } diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java index 48694005e01..75595ffbe60 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java @@ -352,23 +352,24 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { version.onTable("HFJ_SEARCH_PARM").dropThisTable("20190814.8"); - version.onTable("HFJ_SPIDX_COORDS").modifyColumn("20190814.9", "RES_TYPE").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); - version.onTable("HFJ_SPIDX_DATE").modifyColumn("20190814.10", "RES_TYPE").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); - version.onTable("HFJ_SPIDX_STRING").modifyColumn("20190814.11", "RES_TYPE").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); + // Make some columns non-nullable that were previously nullable - These are marked as failure allowed, since + // SQL Server won't let us change nullability on columns with indexes pointing to them + version.onTable("HFJ_SPIDX_COORDS").modifyColumn("20190814.9", "RES_TYPE").nonNullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); + version.onTable("HFJ_SPIDX_DATE").modifyColumn("20190814.10", "RES_TYPE").nonNullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); + version.onTable("HFJ_SPIDX_STRING").modifyColumn("20190814.11", "RES_TYPE").nonNullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); version.onTable("HFJ_SPIDX_STRING").addColumn("20190814.12", "HASH_IDENTITY").nullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.LONG); version.onTable("HFJ_SPIDX_STRING").addIndex("20190814.13", "IDX_SP_STRING_HASH_IDENT").unique(false).withColumns("HASH_IDENTITY"); - version.onTable("HFJ_SPIDX_COORDS").modifyColumn("20190814.14", "RES_TYPE").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); - version.onTable("HFJ_SPIDX_QUANTITY").modifyColumn("20190814.15", "RES_TYPE").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); + version.onTable("HFJ_SPIDX_COORDS").modifyColumn("20190814.14", "RES_TYPE").nonNullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); + version.onTable("HFJ_SPIDX_QUANTITY").modifyColumn("20190814.15", "RES_TYPE").nonNullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); version.onTable("HFJ_SPIDX_QUANTITY").dropColumn("20190814.16", "HASH_UNITS_AND_VALPREFIX"); version.onTable("HFJ_SPIDX_QUANTITY").dropColumn("20190814.17", "HASH_VALPREFIX"); - version.onTable("HFJ_SPIDX_NUMBER").modifyColumn("20190814.18", "RES_TYPE").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); - version.onTable("HFJ_SPIDX_TOKEN").modifyColumn("20190814.19", "RES_TYPE").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); - version.onTable("HFJ_SPIDX_URI").modifyColumn("20190814.20", "RES_TYPE").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); - version.onTable("HFJ_SPIDX_URI").modifyColumn("20190814.21", "SP_URI").nullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 254); - - version.onTable("TRM_CODESYSTEM").modifyColumn("20190814.22", "CODE_SYSTEM_URI").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 200); - version.onTable("TRM_CODESYSTEM").modifyColumn("20190814.23", "CS_NAME").nullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 200); - version.onTable("TRM_CODESYSTEM_VER").modifyColumn("20190814.24", "CS_VERSION_ID").nullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 200); + version.onTable("HFJ_SPIDX_NUMBER").modifyColumn("20190814.18", "RES_TYPE").nonNullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); + version.onTable("HFJ_SPIDX_TOKEN").modifyColumn("20190814.19", "RES_TYPE").nonNullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); + version.onTable("HFJ_SPIDX_URI").modifyColumn("20190814.20", "RES_TYPE").nonNullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); + version.onTable("HFJ_SPIDX_URI").modifyColumn("20190814.21", "SP_URI").nullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 254); + version.onTable("TRM_CODESYSTEM").modifyColumn("20190814.22", "CODE_SYSTEM_URI").nonNullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 200); + version.onTable("TRM_CODESYSTEM").modifyColumn("20190814.23", "CS_NAME").nullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 200); + version.onTable("TRM_CODESYSTEM_VER").modifyColumn("20190814.24", "CS_VERSION_ID").nullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 200); } diff --git a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/SchemaMigratorTest.java b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/SchemaMigratorTest.java index 402ee552ea4..cfb830fd85f 100644 --- a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/SchemaMigratorTest.java +++ b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/SchemaMigratorTest.java @@ -3,24 +3,32 @@ package ca.uhn.fhir.jpa.migrate; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.jpa.migrate.taskdef.AddTableRawSqlTask; import ca.uhn.fhir.jpa.migrate.taskdef.BaseTest; +import com.google.common.collect.ImmutableList; +import org.hamcrest.Matchers; +import org.junit.Assert; import org.junit.Test; +import org.mockito.ArgumentMatchers; +import java.sql.SQLException; import java.util.Collections; import java.util.Properties; +import java.util.Set; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.contains; public class SchemaMigratorTest extends BaseTest { @Test - public void migrationRequired() { + public void testMigrationRequired() { AddTableRawSqlTask task = new AddTableRawSqlTask("1", "1"); task.setTableName("SOMETABLE"); task.addSql(DriverTypeEnum.H2_EMBEDDED, "create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255))"); - getMigrator().addTask(task); - SchemaMigrator schemaMigrator = new SchemaMigrator(SchemaMigrator.HAPI_FHIR_MIGRATION_TABLENAME, getDataSource(), new Properties(), Collections.singletonList(task)); + SchemaMigrator schemaMigrator = new SchemaMigrator(SchemaMigrator.HAPI_FHIR_MIGRATION_TABLENAME, getDataSource(), new Properties(), ImmutableList.of(task)); + schemaMigrator.setDriverType(DriverTypeEnum.H2_EMBEDDED); try { schemaMigrator.validate(); @@ -28,7 +36,34 @@ public class SchemaMigratorTest extends BaseTest { } catch (ConfigurationException e) { assertEquals("The database schema for " + getUrl() + " is out of date. Current database schema version is unknown. Schema version required by application is " + task.getFlywayVersion() + ". Please run the database migrator.", e.getMessage()); } - getMigrator().migrate(); + + schemaMigrator.migrate(); + schemaMigrator.validate(); } + + + @Test + public void testMigrationRequiredNoFlyway() throws SQLException { + AddTableRawSqlTask task = new AddTableRawSqlTask("1", "1"); + task.setTableName("SOMETABLE"); + task.addSql(DriverTypeEnum.H2_EMBEDDED, "create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255))"); + + SchemaMigrator schemaMigrator = new SchemaMigrator(SchemaMigrator.HAPI_FHIR_MIGRATION_TABLENAME, getDataSource(), new Properties(), ImmutableList.of(task)); + schemaMigrator.setDriverType(DriverTypeEnum.H2_EMBEDDED); + schemaMigrator.setDontUseFlyway(true); + + // Validate shouldn't fail if we aren't using Flyway + schemaMigrator.validate(); + + schemaMigrator.migrate(); + + schemaMigrator.validate(); + + DriverTypeEnum.ConnectionProperties connectionProperties = DriverTypeEnum.H2_EMBEDDED.newConnectionProperties(getDataSource().getUrl(), getDataSource().getUsername(), getDataSource().getPassword()); + Set tableNames = JdbcUtils.getTableNames(connectionProperties); + assertThat(tableNames, Matchers.contains("SOMETABLE")); + + } + } diff --git a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/DropTableTest.java b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/DropTableTest.java index 0b785cf4102..16350002f9f 100644 --- a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/DropTableTest.java +++ b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/DropTableTest.java @@ -67,9 +67,9 @@ public class DropTableTest extends BaseTest { assertThat(JdbcUtils.getTableNames(getConnectionProperties()), (hasItems("SOMETABLE"))); - assertThat(getMigrator().getMigrationInfo().pending().length, greaterThan(0)); + assertThat(getMigrator().getMigrationInfo().get().pending().length, greaterThan(0)); getMigrator().migrate(); - assertThat(getMigrator().getMigrationInfo().pending().length, equalTo(0)); + assertThat(getMigrator().getMigrationInfo().get().pending().length, equalTo(0)); assertThat(JdbcUtils.getTableNames(getConnectionProperties()), not(hasItems("SOMETABLE"))); }