Allow non-flyway migration

This commit is contained in:
James Agnew 2019-12-05 10:46:37 -05:00
parent b2e2cb88d7
commit 69a5266127
10 changed files with 299 additions and 101 deletions

View File

@ -20,8 +20,11 @@ package ca.uhn.fhir.cli;
* #L% * #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.DriverTypeEnum;
import ca.uhn.fhir.jpa.migrate.FlywayMigrator; 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.CommandLine;
import org.apache.commons.cli.Options; import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException; import org.apache.commons.cli.ParseException;
@ -35,10 +38,14 @@ import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.defaultString; 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<T extends Enum> extends BaseCommand { public abstract class BaseFlywayMigrateDatabaseCommand<T extends Enum> extends BaseCommand {
public static final String MIGRATE_DATABASE = "migrate-database"; public static final String MIGRATE_DATABASE = "migrate-database";
public static final String DONT_USE_FLYWAY = "dont-use-flyway";
private Set<String> myFlags; private Set<String> myFlags;
private String myMigrationTableName; private String myMigrationTableName;
@ -78,6 +85,7 @@ public abstract class BaseFlywayMigrateDatabaseCommand<T extends Enum> extends B
addRequiredOption(retVal, "p", "password", "Password", "The JDBC database password"); addRequiredOption(retVal, "p", "password", "Password", "The JDBC database password");
addRequiredOption(retVal, "d", "driver", "Driver", "The database driver to use (Options are " + driverOptions() + ")"); 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, "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."); 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; return retVal;
@ -110,7 +118,14 @@ public abstract class BaseFlywayMigrateDatabaseCommand<T extends Enum> extends B
.filter(StringUtils::isNotBlank) .filter(StringUtils::isNotBlank)
.collect(Collectors.toSet()); .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.setConnectionUrl(url);
migrator.setDriverType(driverType); migrator.setDriverType(driverType);
migrator.setUsername(username); migrator.setUsername(username);
@ -121,7 +136,7 @@ public abstract class BaseFlywayMigrateDatabaseCommand<T extends Enum> extends B
migrator.migrate(); migrator.migrate();
} }
protected abstract void addTasks(FlywayMigrator theMigrator); protected abstract void addTasks(BaseMigrator theMigrator);
public void setMigrationTableName(String theMigrationTableName) { public void setMigrationTableName(String theMigrationTableName) {
myMigrationTableName = theMigrationTableName; myMigrationTableName = theMigrationTableName;

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.cli;
* #L% * #L%
*/ */
import ca.uhn.fhir.jpa.migrate.BaseMigrator;
import ca.uhn.fhir.jpa.migrate.FlywayMigrator; import ca.uhn.fhir.jpa.migrate.FlywayMigrator;
import ca.uhn.fhir.jpa.migrate.SchemaMigrator; import ca.uhn.fhir.jpa.migrate.SchemaMigrator;
import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask; import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask;
@ -44,7 +45,7 @@ public class HapiFlywayMigrateDatabaseCommand extends BaseFlywayMigrateDatabaseC
} }
@Override @Override
protected void addTasks(FlywayMigrator theMigrator) { protected void addTasks(BaseMigrator theMigrator) {
List<BaseTask<?>> tasks = new HapiFhirJpaMigrationTasks(getFlags()).getAllTasks(VersionEnum.values()); List<BaseTask<?>> tasks = new HapiFhirJpaMigrationTasks(getFlags()).getAllTasks(VersionEnum.values());
theMigrator.addTasks(tasks); theMigrator.addTasks(tasks);
} }

View File

@ -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<Map<String, Object>> 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 @Test
public void testMigrateFromEmptySchema() throws IOException, SQLException { public void testMigrateFromEmptySchema() throws IOException, SQLException {

View File

@ -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<MigrationInfoService> getMigrationInfo();
public abstract void addTasks(List<BaseTask<?>> 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;
}
}

View File

@ -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<BaseTask<?>> 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<MigrationInfoService> getMigrationInfo() {
return Optional.empty();
}
@Override
public void addTasks(List<BaseTask<?>> theMigrationTasks) {
myTasks.addAll(theMigrationTasks);
}
}

View File

@ -31,34 +31,30 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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 static final Logger ourLog = LoggerFactory.getLogger(FlywayMigrator.class);
private DriverTypeEnum myDriverType;
private final String myMigrationTableName; private final String myMigrationTableName;
private String myConnectionUrl;
private String myUsername;
private String myPassword;
private List<FlywayMigration> myTasks = new ArrayList<>(); private List<FlywayMigration> myTasks = new ArrayList<>();
private boolean myDryRun;
private boolean myNoColumnShrink;
public FlywayMigrator(String theMigrationTableName, BasicDataSource theDataSource) { public FlywayMigrator(String theMigrationTableName, BasicDataSource theDataSource) {
this(theMigrationTableName); this(theMigrationTableName);
myConnectionUrl = theDataSource.getUrl(); setConnectionUrl(theDataSource.getUrl());
myUsername = theDataSource.getUsername(); setUsername(theDataSource.getUsername());
myPassword = theDataSource.getPassword(); setPassword(theDataSource.getPassword());
String driverClassName = theDataSource.getDriverClassName(); String driverClassName = theDataSource.getDriverClassName();
if (driverClassName == null) { if (driverClassName == null) {
ourLog.error(this.getClass().getSimpleName() + " constructed without a database driver"); ourLog.error(this.getClass().getSimpleName() + " constructed without a database driver");
} else { } else {
myDriverType = DriverTypeEnum.fromDriverClassName(driverClassName); DriverTypeEnum driverType = DriverTypeEnum.fromDriverClassName(driverClassName);
if (myDriverType == null) { if (driverType == null) {
ourLog.error("Unknown driver class " + driverClassName); ourLog.error("Unknown driver class " + driverClassName);
} }
setDriverType(driverType);
} }
} }
@ -66,32 +62,13 @@ public class FlywayMigrator {
myMigrationTableName = theMigrationTableName; 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) { public void addTask(BaseTask<?> theTask) {
myTasks.add(new FlywayMigration(theTask, this)); myTasks.add(new FlywayMigration(theTask, this));
} }
public void setDryRun(boolean theDryRun) { @Override
myDryRun = theDryRun;
}
public void migrate() { 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 flyway = initFlyway(connectionProperties);
flyway.migrate(); flyway.migrate();
} catch (Exception e) { } 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 // TODO KHS Is there a way we can use datasource instead of url, username, password here
Flyway flyway = Flyway.configure() Flyway flyway = Flyway.configure()
.table(myMigrationTableName) .table(myMigrationTableName)
.dataSource(myConnectionUrl, myUsername, myPassword) .dataSource(getConnectionUrl(), getUsername(), getPassword())
.baselineOnMigrate(true) .baselineOnMigrate(true)
.javaMigrations(myTasks.toArray(new JavaMigration[0])) .javaMigrations(myTasks.toArray(new JavaMigration[0]))
.load(); .load();
@ -113,45 +90,19 @@ public class FlywayMigrator {
return flyway; return flyway;
} }
@Override
public void addTasks(List<BaseTask<?>> theTasks) { public void addTasks(List<BaseTask<?>> theTasks) {
theTasks.forEach(this::addTask); theTasks.forEach(this::addTask);
} }
public void setNoColumnShrink(boolean theNoColumnShrink) { @Override
myNoColumnShrink = theNoColumnShrink; public Optional<MigrationInfoService> getMigrationInfo() {
} if (getDriverType() == null) {
return Optional.empty();
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;
} }
try (DriverTypeEnum.ConnectionProperties connectionProperties = myDriverType.newConnectionProperties(myConnectionUrl, myUsername, myPassword)) { try (DriverTypeEnum.ConnectionProperties connectionProperties = getDriverType().newConnectionProperties(getConnectionUrl(), getUsername(), getPassword())) {
Flyway flyway = initFlyway(connectionProperties); Flyway flyway = initFlyway(connectionProperties);
return flyway.info(); return Optional.of(flyway.info());
} }
} }

View File

@ -32,25 +32,36 @@ import org.slf4j.LoggerFactory;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Properties; import java.util.Properties;
public class SchemaMigrator { public class SchemaMigrator {
private static final Logger ourLog = LoggerFactory.getLogger(SchemaMigrator.class);
public static final String HAPI_FHIR_MIGRATION_TABLENAME = "FLY_HFJ_MIGRATION"; 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 BasicDataSource myDataSource;
private final FlywayMigrator myMigrator;
private final boolean mySkipValidation; private final boolean mySkipValidation;
private final String myMigrationTableName;
private final List<BaseTask<?>> myMigrationTasks;
private boolean myDontUseFlyway;
private DriverTypeEnum myDriverType;
/**
* Constructor
*/
public SchemaMigrator(String theMigrationTableName, BasicDataSource theDataSource, Properties jpaProperties, List<BaseTask<?>> theMigrationTasks) { public SchemaMigrator(String theMigrationTableName, BasicDataSource theDataSource, Properties jpaProperties, List<BaseTask<?>> theMigrationTasks) {
myDataSource = theDataSource; myDataSource = theDataSource;
myMigrationTableName = theMigrationTableName;
myMigrationTasks = theMigrationTasks;
if (jpaProperties.containsKey(AvailableSettings.HBM2DDL_AUTO) && "update".equals(jpaProperties.getProperty(AvailableSettings.HBM2DDL_AUTO))) { if (jpaProperties.containsKey(AvailableSettings.HBM2DDL_AUTO) && "update".equals(jpaProperties.getProperty(AvailableSettings.HBM2DDL_AUTO))) {
mySkipValidation = true; mySkipValidation = true;
} else { } else {
mySkipValidation = false; mySkipValidation = false;
} }
myMigrator = new FlywayMigrator(theMigrationTableName, theDataSource); }
myMigrator.addTasks(theMigrationTasks);
public void setDontUseFlyway(boolean theDontUseFlyway) {
myDontUseFlyway = theDontUseFlyway;
} }
public void validate() { public void validate() {
@ -59,13 +70,15 @@ public class SchemaMigrator {
return; return;
} }
try (Connection connection = myDataSource.getConnection()) { try (Connection connection = myDataSource.getConnection()) {
MigrationInfoService migrationInfo = myMigrator.getMigrationInfo(); Optional<MigrationInfoService> migrationInfo = newMigrator().getMigrationInfo();
if (migrationInfo.pending().length > 0) { if (migrationInfo.isPresent()) {
throw new ConfigurationException("The database schema for " + myDataSource.getUrl() + " is out of date. " + if (migrationInfo.get().pending().length > 0) {
"Current database schema version is " + getCurrentVersion(migrationInfo) + ". Schema version required by application is " + throw new ConfigurationException("The database schema for " + myDataSource.getUrl() + " is out of date. " +
getLastVersion(migrationInfo) + ". Please run the database migrator."); "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) { } catch (SQLException e) {
throw new ConfigurationException("Unable to connect to " + myDataSource.toString(), 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."); ourLog.warn("Database running in hibernate auto-update mode. Skipping schema migration.");
return; 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) { private String getCurrentVersion(MigrationInfoService theMigrationInfo) {
@ -94,4 +122,8 @@ public class SchemaMigrator {
} }
return "unknown"; return "unknown";
} }
public void setDriverType(DriverTypeEnum theDriverType) {
myDriverType = theDriverType;
}
} }

View File

@ -352,23 +352,24 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
version.onTable("HFJ_SEARCH_PARM").dropThisTable("20190814.8"); version.onTable("HFJ_SEARCH_PARM").dropThisTable("20190814.8");
version.onTable("HFJ_SPIDX_COORDS").modifyColumn("20190814.9", "RES_TYPE").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); // Make some columns non-nullable that were previously nullable - These are marked as failure allowed, since
version.onTable("HFJ_SPIDX_DATE").modifyColumn("20190814.10", "RES_TYPE").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); // SQL Server won't let us change nullability on columns with indexes pointing to them
version.onTable("HFJ_SPIDX_STRING").modifyColumn("20190814.11", "RES_TYPE").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100); 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").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_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_COORDS").modifyColumn("20190814.14", "RES_TYPE").nonNullable().failureAllowed().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_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.16", "HASH_UNITS_AND_VALPREFIX");
version.onTable("HFJ_SPIDX_QUANTITY").dropColumn("20190814.17", "HASH_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_NUMBER").modifyColumn("20190814.18", "RES_TYPE").nonNullable().failureAllowed().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_TOKEN").modifyColumn("20190814.19", "RES_TYPE").nonNullable().failureAllowed().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.20", "RES_TYPE").nonNullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 100);
version.onTable("HFJ_SPIDX_URI").modifyColumn("20190814.21", "SP_URI").nullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 254); 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.22", "CODE_SYSTEM_URI").nonNullable().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").modifyColumn("20190814.23", "CS_NAME").nullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 200); version.onTable("TRM_CODESYSTEM_VER").modifyColumn("20190814.24", "CS_VERSION_ID").nullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 200);
version.onTable("TRM_CODESYSTEM_VER").modifyColumn("20190814.24", "CS_VERSION_ID").nullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 200);
} }

View File

@ -3,24 +3,32 @@ package ca.uhn.fhir.jpa.migrate;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.jpa.migrate.taskdef.AddTableRawSqlTask; import ca.uhn.fhir.jpa.migrate.taskdef.AddTableRawSqlTask;
import ca.uhn.fhir.jpa.migrate.taskdef.BaseTest; 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.junit.Test;
import org.mockito.ArgumentMatchers;
import java.sql.SQLException;
import java.util.Collections; import java.util.Collections;
import java.util.Properties; import java.util.Properties;
import java.util.Set;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.contains;
public class SchemaMigratorTest extends BaseTest { public class SchemaMigratorTest extends BaseTest {
@Test @Test
public void migrationRequired() { public void testMigrationRequired() {
AddTableRawSqlTask task = new AddTableRawSqlTask("1", "1"); AddTableRawSqlTask task = new AddTableRawSqlTask("1", "1");
task.setTableName("SOMETABLE"); task.setTableName("SOMETABLE");
task.addSql(DriverTypeEnum.H2_EMBEDDED, "create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255))"); 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 { try {
schemaMigrator.validate(); schemaMigrator.validate();
@ -28,7 +36,34 @@ public class SchemaMigratorTest extends BaseTest {
} catch (ConfigurationException e) { } 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()); 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(); 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<String> tableNames = JdbcUtils.getTableNames(connectionProperties);
assertThat(tableNames, Matchers.contains("SOMETABLE"));
}
} }

View File

@ -67,9 +67,9 @@ public class DropTableTest extends BaseTest {
assertThat(JdbcUtils.getTableNames(getConnectionProperties()), (hasItems("SOMETABLE"))); assertThat(JdbcUtils.getTableNames(getConnectionProperties()), (hasItems("SOMETABLE")));
assertThat(getMigrator().getMigrationInfo().pending().length, greaterThan(0)); assertThat(getMigrator().getMigrationInfo().get().pending().length, greaterThan(0));
getMigrator().migrate(); getMigrator().migrate();
assertThat(getMigrator().getMigrationInfo().pending().length, equalTo(0)); assertThat(getMigrator().getMigrationInfo().get().pending().length, equalTo(0));
assertThat(JdbcUtils.getTableNames(getConnectionProperties()), not(hasItems("SOMETABLE"))); assertThat(JdbcUtils.getTableNames(getConnectionProperties()), not(hasItems("SOMETABLE")));
} }