From 45bab9879f5965bbb7dcd73726658122ebfcdbb1 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Fri, 6 Dec 2019 19:21:30 -0500 Subject: [PATCH 01/13] migration failed when no-column-shrink was enabled. --- .../jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 75595ffbe60..38f30caf23c 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 @@ -163,12 +163,12 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { .withColumns("VALUESET_PID", "VALUESET_ORDER"); // Account for RESTYPE_LEN column increasing from 30 to 40 - version.onTable("HFJ_RESOURCE").modifyColumn("20191002.1", "RES_TYPE").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 40); - version.onTable("HFJ_RES_VER").modifyColumn("20191002.2", "RES_TYPE").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 40); - version.onTable("HFJ_HISTORY_TAG").modifyColumn("20191002.3", "RES_TYPE").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 40); - version.onTable("HFJ_RES_LINK").modifyColumn("20191002.4", "SOURCE_RESOURCE_TYPE").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 40); - version.onTable("HFJ_RES_LINK").modifyColumn("20191002.5", "TARGET_RESOURCE_TYPE").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 40); - version.onTable("HFJ_RES_TAG").modifyColumn("20191002.6", "RES_TYPE").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 40); + version.onTable("HFJ_RESOURCE").modifyColumn("20191002.1", "RES_TYPE").nonNullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 40); + version.onTable("HFJ_RES_VER").modifyColumn("20191002.2", "RES_TYPE").nonNullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 40); + version.onTable("HFJ_HISTORY_TAG").modifyColumn("20191002.3", "RES_TYPE").nonNullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 40); + version.onTable("HFJ_RES_LINK").modifyColumn("20191002.4", "SOURCE_RESOURCE_TYPE").nonNullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 40); + version.onTable("HFJ_RES_LINK").modifyColumn("20191002.5", "TARGET_RESOURCE_TYPE").nonNullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 40); + version.onTable("HFJ_RES_TAG").modifyColumn("20191002.6", "RES_TYPE").nonNullable().failureAllowed().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 40); // TermConceptDesignation version.startSectionWithMessage("Processing table: TRM_CONCEPT_DESIG"); From 976740955c117d69c95ac61bc181ee3e5985ff9f Mon Sep 17 00:00:00 2001 From: Jafer Khan Date: Sat, 7 Dec 2019 18:08:45 +0500 Subject: [PATCH 02/13] Make constructor of 'Verdict' public. Fixes gh-1621 --- .../auth/AuthorizationInterceptor.java | 2 +- .../ca/uhn/fhir/rest/server/VerdictTest.java | 48 +++++++++++++++++++ .../server/interceptor/auth/VerdictTest.java | 2 + 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/VerdictTest.java diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java index b001ba9dff6..a74dbe83a96 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java @@ -423,7 +423,7 @@ public class AuthorizationInterceptor implements IRuleApplier { private final IAuthRule myDecidingRule; private final PolicyEnum myDecision; - Verdict(PolicyEnum theDecision, IAuthRule theDecidingRule) { + public Verdict(PolicyEnum theDecision, IAuthRule theDecidingRule) { Validate.notNull(theDecision); myDecision = theDecision; diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/VerdictTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/VerdictTest.java new file mode 100644 index 00000000000..1589dd75f82 --- /dev/null +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/VerdictTest.java @@ -0,0 +1,48 @@ +package ca.uhn.fhir.rest.server; + +import ca.uhn.fhir.interceptor.api.Pointcut; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationFlagsEnum; +import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor.Verdict; +import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule; +import ca.uhn.fhir.rest.server.interceptor.auth.IRuleApplier; +import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.junit.Test; + +import java.util.Set; + +/** + * Tests for {@link Verdict} + * + * @author Jafer Khan Shamshad + */ +public class VerdictTest { + + /** + * Implementers should be able to instantiate {@link Verdict} outside the package where it has been defined. + */ + @Test + public void testInstantiationFromAnotherPackage() { + Verdict verdict = new Verdict(PolicyEnum.ALLOW, new CustomRule()); + } + + /** + * Existing implementations of {@link IAuthRule} are inaccessible from this package. + * This test class is a sample implementation of {@link IAuthRule}. + */ + public static class CustomRule implements IAuthRule { + + @Override + public Verdict applyRule(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IBaseResource theInputResource, IIdType theInputResourceId, IBaseResource theOutputResource, IRuleApplier theRuleApplier, Set theFlags, Pointcut thePointcut) { + return new Verdict(PolicyEnum.ALLOW, this); + } + + @Override + public String getName() { + return "Custom rule"; + } + } +} diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/VerdictTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/VerdictTest.java index 8c4cd226737..d7d16fc280e 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/VerdictTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/VerdictTest.java @@ -1,9 +1,11 @@ package ca.uhn.fhir.rest.server.interceptor.auth; import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor.Verdict; +import org.junit.Test; public class VerdictTest { + @Test public void testToString() { Verdict v = new AuthorizationInterceptor.Verdict(PolicyEnum.ALLOW, new RuleImplOp("foo")); v.toString(); From 4a70f7a602c3094635b09ea95aa4781aa83e6300 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Sat, 7 Dec 2019 13:52:58 -0500 Subject: [PATCH 03/13] Credit for #1624 --- src/changes/changes.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index e261d7632cb..2d222798906 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -54,6 +54,10 @@ A ConcurrentModificationException was sometimes thrown when performing a cascading delete. This has been corrected. + + The constructor for Verdict.java was inadvertantly made private, preventing custom + rules from being written. Thanks to Jafer Khan for the pull request! + From 0b30033b0a5b94c370d73a65f8fdda0436b4cfce Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Mon, 9 Dec 2019 11:19:18 -0500 Subject: [PATCH 04/13] upgrade flyway to latest version automatically repair failed migrations --- .../uhn/fhir/jpa/migrate/FlywayMigrator.java | 1 + .../fhir/jpa/migrate/SchemaMigratorTest.java | 49 ++++++++++++------- pom.xml | 2 +- 3 files changed, 32 insertions(+), 20 deletions(-) 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 598e4108128..7a4dd0c48bd 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 @@ -70,6 +70,7 @@ public class FlywayMigrator extends BaseMigrator { public void migrate() { try (DriverTypeEnum.ConnectionProperties connectionProperties = getDriverType().newConnectionProperties(getConnectionUrl(), getUsername(), getPassword())) { Flyway flyway = initFlyway(connectionProperties); + flyway.repair(); flyway.migrate(); } catch (Exception e) { throw e; 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 cfb830fd85f..4631d4afa32 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 @@ -4,37 +4,29 @@ 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.flywaydb.core.api.FlywayException; import org.hamcrest.Matchers; -import org.junit.Assert; import org.junit.Test; -import org.mockito.ArgumentMatchers; +import javax.annotation.Nonnull; 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; +import static org.junit.Assert.*; public class SchemaMigratorTest extends BaseTest { + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SchemaMigratorTest.class); @Test 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))"); - - SchemaMigrator schemaMigrator = new SchemaMigrator(SchemaMigrator.HAPI_FHIR_MIGRATION_TABLENAME, getDataSource(), new Properties(), ImmutableList.of(task)); - schemaMigrator.setDriverType(DriverTypeEnum.H2_EMBEDDED); + SchemaMigrator schemaMigrator = createSchemaMigrator("create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255))"); try { schemaMigrator.validate(); fail(); } 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 1.1. Please run the database migrator.", e.getMessage()); } schemaMigrator.migrate(); @@ -44,12 +36,22 @@ public class SchemaMigratorTest extends BaseTest { @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))"); + public void testRepairFailedMigration() { + SchemaMigrator schemaMigrator = createSchemaMigrator("create fable SOMETABLE (PID bigint not null, TEXTCOL varchar(255))"); + try { + schemaMigrator.migrate(); + fail(); + } catch (FlywayException e) { + assertEquals(org.springframework.jdbc.BadSqlGrammarException.class, e.getCause().getCause().getClass()); + } + schemaMigrator = createSchemaMigrator("create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255))"); + schemaMigrator.migrate(); + } - SchemaMigrator schemaMigrator = new SchemaMigrator(SchemaMigrator.HAPI_FHIR_MIGRATION_TABLENAME, getDataSource(), new Properties(), ImmutableList.of(task)); + + @Test + public void testMigrationRequiredNoFlyway() throws SQLException { + SchemaMigrator schemaMigrator = createSchemaMigrator("create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255))"); schemaMigrator.setDriverType(DriverTypeEnum.H2_EMBEDDED); schemaMigrator.setDontUseFlyway(true); @@ -66,4 +68,13 @@ public class SchemaMigratorTest extends BaseTest { } + @Nonnull + private SchemaMigrator createSchemaMigrator(String theSql) { + AddTableRawSqlTask task = new AddTableRawSqlTask("1", "1"); + task.setTableName("SOMETABLE"); + task.addSql(DriverTypeEnum.H2_EMBEDDED, theSql); + SchemaMigrator retval = new SchemaMigrator(SchemaMigrator.HAPI_FHIR_MIGRATION_TABLENAME, getDataSource(), new Properties(), ImmutableList.of(task)); + retval.setDriverType(DriverTypeEnum.H2_EMBEDDED); + return retval; + } } diff --git a/pom.xml b/pom.xml index e9517296c48..97d58ac1241 100644 --- a/pom.xml +++ b/pom.xml @@ -620,7 +620,7 @@ 9.4.24.v20191120 3.0.2 - 6.0.8 + 6.1.0 5.4.6.Final From ec37138138eaeabbac8a23807aa42871ff0abb64 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Mon, 9 Dec 2019 11:32:25 -0500 Subject: [PATCH 05/13] add environment variable to allow flyway out of order migration --- .../uhn/fhir/jpa/migrate/FlywayMigrator.java | 5 +++ .../fhir/jpa/migrate/SchemaMigratorTest.java | 37 +++++++++++++++---- 2 files changed, 35 insertions(+), 7 deletions(-) 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 7a4dd0c48bd..947bef342b6 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 @@ -33,9 +33,12 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import static org.apache.commons.lang3.StringUtils.isNotBlank; + public class FlywayMigrator extends BaseMigrator { private static final Logger ourLog = LoggerFactory.getLogger(FlywayMigrator.class); + public static final String OUT_OF_ORDER_MIGRATION = "OUT_OF_ORDER_MIGRATION"; private final String myMigrationTableName; private List myTasks = new ArrayList<>(); @@ -79,10 +82,12 @@ public class FlywayMigrator extends BaseMigrator { private Flyway initFlyway(DriverTypeEnum.ConnectionProperties theConnectionProperties) { // TODO KHS Is there a way we can use datasource instead of url, username, password here + boolean outOfOrder = isNotBlank(System.getProperty(OUT_OF_ORDER_MIGRATION)); Flyway flyway = Flyway.configure() .table(myMigrationTableName) .dataSource(getConnectionUrl(), getUsername(), getPassword()) .baselineOnMigrate(true) + .outOfOrder(outOfOrder) .javaMigrations(myTasks.toArray(new JavaMigration[0])) .load(); for (FlywayMigration task : myTasks) { 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 4631d4afa32..d778c511baa 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 @@ -13,6 +13,7 @@ import java.sql.SQLException; import java.util.Properties; import java.util.Set; +import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.*; public class SchemaMigratorTest extends BaseTest { @@ -20,7 +21,7 @@ public class SchemaMigratorTest extends BaseTest { @Test public void testMigrationRequired() { - SchemaMigrator schemaMigrator = createSchemaMigrator("create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255))"); + SchemaMigrator schemaMigrator = createTableMigrator(); try { schemaMigrator.validate(); @@ -37,21 +38,38 @@ public class SchemaMigratorTest extends BaseTest { @Test public void testRepairFailedMigration() { - SchemaMigrator schemaMigrator = createSchemaMigrator("create fable SOMETABLE (PID bigint not null, TEXTCOL varchar(255))"); + SchemaMigrator schemaMigrator = createSchemaMigrator("SOMETABLE", "create fable SOMETABLE (PID bigint not null, TEXTCOL varchar(255))", "1"); try { schemaMigrator.migrate(); fail(); } catch (FlywayException e) { assertEquals(org.springframework.jdbc.BadSqlGrammarException.class, e.getCause().getCause().getClass()); } - schemaMigrator = createSchemaMigrator("create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255))"); + schemaMigrator = createTableMigrator(); + schemaMigrator.migrate(); + } + + @Test + public void testOutOfOrderMigration() { + SchemaMigrator schemaMigrator = createSchemaMigrator("SOMETABLE", "create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255))", "2"); + schemaMigrator.migrate(); + + schemaMigrator = createSchemaMigrator("SOMETABLE" ,"create table SOMEOTHERTABLE (PID bigint not null, TEXTCOL varchar(255))", "1"); + + try { + schemaMigrator.migrate(); + fail(); + } catch (FlywayException e) { + assertThat(e.getMessage(), containsString("Detected resolved migration not applied to database: 1.1")); + } + System.setProperty(FlywayMigrator.OUT_OF_ORDER_MIGRATION, "true"); schemaMigrator.migrate(); } @Test public void testMigrationRequiredNoFlyway() throws SQLException { - SchemaMigrator schemaMigrator = createSchemaMigrator("create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255))"); + SchemaMigrator schemaMigrator = createTableMigrator(); schemaMigrator.setDriverType(DriverTypeEnum.H2_EMBEDDED); schemaMigrator.setDontUseFlyway(true); @@ -69,9 +87,14 @@ public class SchemaMigratorTest extends BaseTest { } @Nonnull - private SchemaMigrator createSchemaMigrator(String theSql) { - AddTableRawSqlTask task = new AddTableRawSqlTask("1", "1"); - task.setTableName("SOMETABLE"); + private SchemaMigrator createTableMigrator() { + return createSchemaMigrator("SOMETABLE", "create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255))", "1"); + } + + @Nonnull + private SchemaMigrator createSchemaMigrator(String theTableName, String theSql, String theSchemaVersion) { + AddTableRawSqlTask task = new AddTableRawSqlTask("1", theSchemaVersion); + task.setTableName(theTableName); task.addSql(DriverTypeEnum.H2_EMBEDDED, theSql); SchemaMigrator retval = new SchemaMigrator(SchemaMigrator.HAPI_FHIR_MIGRATION_TABLENAME, getDataSource(), new Properties(), ImmutableList.of(task)); retval.setDriverType(DriverTypeEnum.H2_EMBEDDED); From c1aec7c0e461be56bf82739d14e44b7cb00dbd17 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Mon, 9 Dec 2019 11:45:30 -0500 Subject: [PATCH 06/13] document new environment variable --- .../hapi/fhir/docs/server_jpa/upgrading.md | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/upgrading.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/upgrading.md index 29b046133f8..ed452a9959b 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/upgrading.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/upgrading.md @@ -6,10 +6,10 @@ When upgrading the JPA server from one version of HAPI FHIR to a newer version, Note that this feature was added in HAPI FHIR 3.5.0. It is not able to migrate from versions prior to HAPI FHIR 3.4.0. **Please make a backup of your database before running this command!** -The following example shows how to use the migrator utility to migrate between two versions. +The following example shows how to use the migrator utility to migrate to the latest version. ```bash -./hapi-fhir-cli migrate-database -d DERBY_EMBEDDED -u "jdbc:derby:directory:target/jpaserver_derby_files;create=true" -n "" -p "" -f V3_4_0 -t V3_5_0 +./hapi-fhir-cli migrate-database -d H2_EMBEDDED -u "jdbc:derby:directory:target/jpaserver_derby_files;create=true" -n "" -p "" ``` You may use the following command to get detailed help on the options: @@ -21,15 +21,13 @@ You may use the following command to get detailed help on the options: Note the arguments: * `-d [dialect]` – This indicates the database dialect to use. See the detailed help for a list of options -* `-f [version]` – The version to migrate from -* `-t [version]` – The version to migrate to # Oracle Support Note that the Oracle JDBC drivers are not distributed in the Maven Central repository, so they are not included in HAPI FHIR. In order to use this command with an Oracle database, you will need to invoke the CLI as follows: ```bash -java -cp hapi-fhir-cli.jar ca.uhn.fhir.cli.App migrate-database -d ORACLE_12C -u "[url]" -n "[username]" -p "[password]" -f V3_4_0 -t V3_5_0 +java -cp hapi-fhir-cli.jar ca.uhn.fhir.cli.App migrate-database -d ORACLE_12C -u "[url]" -n "[username]" -p "[password]" ``` ## Migrating 3.4.0 to 3.5.0+ @@ -48,7 +46,7 @@ In order to perform a migration using this functionality, the following steps sh * Run the database migrator command, including the entry `-x no-migrate-350-hashes` on the command line. For example: ``` -./hapi-fhir-cli migrate-database -d DERBY_EMBEDDED -u "jdbc:derby:directory:target/jpaserver_derby_files;create=true" -n "" -p "" -f V3_4_0 -t V3_6_0 -x no-migrate-350-hashes +./hapi-fhir-cli migrate-database -d H2_EMBEDDED -u "jdbc:h2:directory:target/jpaserver_h2_files;create=true" -n "" -p "" -x no-migrate-350-hashes ``` * Rebuild and start your HAPI FHIR JPA server. At this point you should have a working HAPI FHIR JPA 3.6.0 server that is is still using HAPI FHIR 3.4.0 search indexes. Search hashes will be generated for any newly created or updated data but existing data will have null hashes. @@ -66,6 +64,14 @@ SELECT * FROM HFJ_RES_REINDEX_JOB * Execute the migrator tool again, this time omitting the flag option, e.g. ```bash -./hapi-fhir-cli migrate-database -d DERBY_EMBEDDED -u "jdbc:derby:directory:target/jpaserver_derby_files;create=true" -n "" -p "" -f V3_4_0 -t V3_6_0 +./hapi-fhir-cli migrate-database -d DERBY_EMBEDDED -u "jdbc:h2:directory:target/jpaserver_h2_files;create=true" -n "" -p "" ``` * Rebuild, and start HAPI FHIR JPA again. + +# Flyway + +In version 4.2.0 onwards, HAPI FHIR JPA uses Flyway for schema migrations. The "from" and "to" parameters are no longer used. Flyway maintains a list of completed migrations in a table called `FLY_HFJ_MIGRATION`. When you run the migration command, flyway scans the list of completed migrations in this table and compares them to the list of known migrations, and runs only the new ones. + +## Recovering from a failed migration + +Under certain unlikely scenarios, you may see the error message "Detected resolved migration not applied to database". This means that Flyway has detected a new migration task that has an earlier version than the latest version in the Flyway table. You can set the environment variable `OUT_OF_ORDER_MIGRATION` to allow Flyway to run migration tasks out of order. From 5117b93add0da71595bac12402b476a0e615fc81 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Mon, 9 Dec 2019 11:49:43 -0500 Subject: [PATCH 07/13] document new environment variable --- .../resources/ca/uhn/hapi/fhir/docs/server_jpa/upgrading.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/upgrading.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/upgrading.md index ed452a9959b..5a1c476ba62 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/upgrading.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/upgrading.md @@ -70,7 +70,7 @@ SELECT * FROM HFJ_RES_REINDEX_JOB # Flyway -In version 4.2.0 onwards, HAPI FHIR JPA uses Flyway for schema migrations. The "from" and "to" parameters are no longer used. Flyway maintains a list of completed migrations in a table called `FLY_HFJ_MIGRATION`. When you run the migration command, flyway scans the list of completed migrations in this table and compares them to the list of known migrations, and runs only the new ones. +As of version 4.2.0, HAPI FHIR JPA now uses Flyway for schema migrations. The "from" and "to" parameters are no longer used. Flyway maintains a list of completed migrations in a table called `FLY_HFJ_MIGRATION`. When you run the migration command, flyway scans the list of completed migrations in this table and compares them to the list of known migrations, and runs only the new ones. ## Recovering from a failed migration From deb5fd40a75debf50bedda74299b4ead71a59dd6 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Mon, 9 Dec 2019 14:42:37 -0500 Subject: [PATCH 08/13] Added stubbed tasks for add and drop index --- .../jpa/migrate/taskdef/AddColumnTask.java | 2 +- .../migrate/taskdef/AddForeignKeyTask.java | 2 +- .../migrate/taskdef/AddIdGeneratorTask.java | 2 +- .../jpa/migrate/taskdef/AddIndexTask.java | 2 +- .../migrate/taskdef/AddTableByColumnTask.java | 2 +- .../migrate/taskdef/AddTableRawSqlTask.java | 2 +- .../jpa/migrate/taskdef/ArbitrarySqlTask.java | 2 +- .../fhir/jpa/migrate/taskdef/BaseTask.java | 21 +++++++++++++++++-- .../migrate/taskdef/CalculateHashesTask.java | 2 +- .../jpa/migrate/taskdef/DropColumnTask.java | 2 +- .../migrate/taskdef/DropForeignKeyTask.java | 2 +- .../migrate/taskdef/DropIdGeneratorTask.java | 2 +- .../jpa/migrate/taskdef/DropIndexTask.java | 2 +- .../jpa/migrate/taskdef/DropTableTask.java | 2 +- .../migrate/taskdef/ExecuteRawSqlTask.java | 2 +- .../migrate/taskdef/InitializeSchemaTask.java | 2 +- .../jpa/migrate/taskdef/ModifyColumnTask.java | 2 +- .../jpa/migrate/taskdef/RenameColumnTask.java | 2 +- .../fhir/jpa/migrate/tasks/api/Builder.java | 20 ++++++++++++++++++ .../taskdef/AddTableByColumnTaskTest.java | 17 +++++++++++---- 20 files changed, 69 insertions(+), 23 deletions(-) diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTask.java index 7af47f1ce08..f9e73700311 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTask.java @@ -42,7 +42,7 @@ public class AddColumnTask extends BaseTableColumnTypeTask { } @Override - public void execute() throws SQLException { + public void doExecute() throws SQLException { Set columnNames = JdbcUtils.getColumnNames(getConnectionProperties(), getTableName()); if (columnNames.contains(getColumnName())) { logInfo(ourLog, "Column {} already exists on table {} - No action performed", getColumnName(), getTableName()); diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddForeignKeyTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddForeignKeyTask.java index eea21b2ef6a..c4dba638a0b 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddForeignKeyTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddForeignKeyTask.java @@ -66,7 +66,7 @@ public class AddForeignKeyTask extends BaseTableColumnTask { } @Override - public void execute() throws SQLException { + public void doExecute() throws SQLException { Set existing = JdbcUtils.getForeignKeys(getConnectionProperties(), myForeignTableName, getTableName()); if (existing.contains(myConstraintName)) { diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIdGeneratorTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIdGeneratorTask.java index 60f9e527a8e..d293174290d 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIdGeneratorTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIdGeneratorTask.java @@ -50,7 +50,7 @@ public class AddIdGeneratorTask extends BaseTask { } @Override - public void execute() throws SQLException { + public void doExecute() throws SQLException { Set tableNames = JdbcUtils.getTableNames(getConnectionProperties()); String sql = null; diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTask.java index add63a1537d..f634c782db9 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTask.java @@ -67,7 +67,7 @@ public class AddIndexTask extends BaseTableTask { } @Override - public void execute() throws SQLException { + public void doExecute() throws SQLException { Set indexNames = JdbcUtils.getIndexNames(getConnectionProperties(), getTableName()); if (indexNames.contains(myIndexName)) { logInfo(ourLog, "Index {} already exists on table {} - No action performed", myIndexName, getTableName()); diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTask.java index 7b41e92668e..38cec37bd74 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTask.java @@ -58,7 +58,7 @@ public class AddTableByColumnTask extends BaseTableTask { } @Override - public void execute() throws SQLException { + public void doExecute() throws SQLException { if (JdbcUtils.getTableNames(getConnectionProperties()).contains(getTableName())) { logInfo(ourLog, "Already have table named {} - No action performed", getTableName()); diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableRawSqlTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableRawSqlTask.java index f67f3628ee1..65f241aae91 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableRawSqlTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableRawSqlTask.java @@ -58,7 +58,7 @@ public class AddTableRawSqlTask extends BaseTableTask { } @Override - public void execute() throws SQLException { + public void doExecute() throws SQLException { Set tableNames = JdbcUtils.getTableNames(getConnectionProperties()); if (tableNames.contains(getTableName())) { logInfo(ourLog, "Table {} already exists - No action performed", getTableName()); diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ArbitrarySqlTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ArbitrarySqlTask.java index 108d35e6960..2d9170b8637 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ArbitrarySqlTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ArbitrarySqlTask.java @@ -63,7 +63,7 @@ public class ArbitrarySqlTask extends BaseTask { } @Override - public void execute() throws SQLException { + public void doExecute() throws SQLException { logInfo(ourLog, "Starting: {}", myDescription); if (StringUtils.isNotBlank(myExecuteOnlyIfTableExists)) { diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTask.java index f05ec1beae4..e74dacadb34 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTask.java @@ -25,7 +25,6 @@ import org.intellij.lang.annotations.Language; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.DataAccessException; -import org.springframework.dao.DataIntegrityViolationException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.support.TransactionTemplate; @@ -47,6 +46,7 @@ public abstract class BaseTask { private String myDescription; private int myChangesCount; private boolean myDryRun; + private boolean myDoNothing; private List myExecutedStatements = new ArrayList<>(); private boolean myNoColumnShrink; private boolean myFailureAllowed; @@ -155,7 +155,15 @@ public abstract class BaseTask { return getConnectionProperties().newJdbcTemplate(); } - public abstract void execute() throws SQLException; + public void execute() throws SQLException { + if (myDoNothing) { + ourLog.info("Skipping stubbed task: {}", getDescription()); + return; + } + doExecute(); + } + + public abstract void doExecute() throws SQLException; public void setFailureAllowed(boolean theFailureAllowed) { myFailureAllowed = theFailureAllowed; @@ -180,6 +188,15 @@ public abstract class BaseTask { } } + public boolean isDoNothing() { + return myDoNothing; + } + + public BaseTask setDoNothing(boolean theDoNothing) { + myDoNothing = theDoNothing; + return this; + } + public static class ExecutedStatement { private final String mySql; private final List myArguments; diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/CalculateHashesTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/CalculateHashesTask.java index 939a479f336..75ef31a94a2 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/CalculateHashesTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/CalculateHashesTask.java @@ -58,7 +58,7 @@ public class CalculateHashesTask extends BaseTableColumnTask { } @Override - public void execute() throws SQLException { + public void doExecute() throws SQLException { Set columnNames = JdbcUtils.getColumnNames(getConnectionProperties(), getTableName()); if (!columnNames.contains(getColumnName())) { logInfo(ourLog, "Column {} does not exist on table {} - No action performed", getColumnName(), getTableName()); diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropForeignKeyTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropForeignKeyTask.java index 6b08912b6f8..0fe92666b69 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropForeignKeyTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropForeignKeyTask.java @@ -65,7 +65,7 @@ public class DropForeignKeyTask extends BaseTableTask { } @Override - public void execute() throws SQLException { + public void doExecute() throws SQLException { Set existing = JdbcUtils.getForeignKeys(getConnectionProperties(), myParentTableName, getTableName()); if (!existing.contains(myConstraintName)) { diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropIdGeneratorTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropIdGeneratorTask.java index dca3b8b195e..dccce05f45f 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropIdGeneratorTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropIdGeneratorTask.java @@ -50,7 +50,7 @@ public class DropIdGeneratorTask extends BaseTask { } @Override - public void execute() throws SQLException { + public void doExecute() throws SQLException { Set tableNames = JdbcUtils.getTableNames(getConnectionProperties()); String sql = null; diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropIndexTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropIndexTask.java index 9f44c48d1e6..21b80a972d3 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropIndexTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropIndexTask.java @@ -53,7 +53,7 @@ public class DropIndexTask extends BaseTableTask { } @Override - public void execute() throws SQLException { + public void doExecute() throws SQLException { Set indexNames = JdbcUtils.getIndexNames(getConnectionProperties(), getTableName()); if (!indexNames.contains(myIndexName)) { diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropTableTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropTableTask.java index 63253a7887b..a5c33b4683a 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropTableTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropTableTask.java @@ -44,7 +44,7 @@ public class DropTableTask extends BaseTableTask { } @Override - public void execute() throws SQLException { + public void doExecute() throws SQLException { Set tableNames = JdbcUtils.getTableNames(getConnectionProperties()); if (!tableNames.contains(getTableName())) { return; diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ExecuteRawSqlTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ExecuteRawSqlTask.java index da0fc07ecf6..04c06fe33ea 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ExecuteRawSqlTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ExecuteRawSqlTask.java @@ -67,7 +67,7 @@ public class ExecuteRawSqlTask extends BaseTask { } @Override - public void execute() { + public void doExecute() { List sqlStatements = myDriverToSqls.computeIfAbsent(getDriverType(), t -> new ArrayList<>()); sqlStatements.addAll(myDriverNeutralSqls); diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTask.java index 7365bd37a8d..c3b1f0832db 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTask.java @@ -48,7 +48,7 @@ public class InitializeSchemaTask extends BaseTask { } @Override - public void execute() throws SQLException { + public void doExecute() throws SQLException { DriverTypeEnum driverType = getDriverType(); Set tableNames = JdbcUtils.getTableNames(getConnectionProperties()); diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTask.java index 2b4a6264ecd..4d3c26dbafe 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTask.java @@ -43,7 +43,7 @@ public class ModifyColumnTask extends BaseTableColumnTypeTask } @Override - public void execute() throws SQLException { + public void doExecute() throws SQLException { JdbcUtils.ColumnType existingType; boolean nullable; diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameColumnTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameColumnTask.java index 5c21080ef43..a3c80db70da 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameColumnTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameColumnTask.java @@ -65,7 +65,7 @@ public class RenameColumnTask extends BaseTableTask { } @Override - public void execute() throws SQLException { + public void doExecute() throws SQLException { Set columnNames = JdbcUtils.getColumnNames(getConnectionProperties(), getTableName()); boolean haveOldName = columnNames.contains(myOldName.toUpperCase()); boolean haveNewName = columnNames.contains(myNewName.toUpperCase()); diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java index 42e0b2a9549..6ae87bd732c 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java @@ -147,9 +147,18 @@ public class Builder { } public void dropIndex(String theVersion, String theIndexName) { + dropIndexOptional(false, theVersion, theIndexName); + } + + public void dropIndexStub(String theVersion, String theIndexName) { + dropIndexOptional(true, theVersion, theIndexName); + } + + private void dropIndexOptional(boolean theDoNothing, String theVersion, String theIndexName) { DropIndexTask task = new DropIndexTask(myRelease, theVersion); task.setIndexName(theIndexName); task.setTableName(myTableName); + task.setDoNothing(theDoNothing); addTask(task); } @@ -244,12 +253,21 @@ public class Builder { myUnique = theUnique; } + public void withColumnsStub(String... theColumnNames) { + withColumnsOptional(true, theColumnNames); + } + public void withColumns(String... theColumnNames) { + withColumnsOptional(false, theColumnNames); + } + + private void withColumnsOptional(boolean theDoNothing, String... theColumnNames) { AddIndexTask task = new AddIndexTask(myRelease, myVersion); task.setTableName(myTableName); task.setIndexName(myIndexName); task.setUnique(myUnique); task.setColumns(theColumnNames); + task.setDoNothing(theDoNothing); addTask(task); } } @@ -355,6 +373,7 @@ public class Builder { private final String myVersion; private final String myColumnName; private final BaseMigrationTasks.IAcceptsTasks myTaskSink; + private boolean myDoNothing; public BuilderAddColumnWithName(String theRelease, String theVersion, String theColumnName, BaseMigrationTasks.IAcceptsTasks theTaskSink) { myRelease = theRelease; @@ -396,6 +415,7 @@ public class Builder { } myTaskSink.addTask(task); } + } } } diff --git a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java index 4b2ee0f79f5..8b5f91596ab 100644 --- a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java +++ b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java @@ -7,12 +7,13 @@ import ca.uhn.fhir.util.VersionEnum; import org.junit.Test; import java.sql.SQLException; +import java.util.Set; +import java.util.stream.Collectors; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.junit.Assert.assertThat; public class AddTableByColumnTaskTest extends BaseTest { - @Test public void testAddTable() throws SQLException { @@ -21,9 +22,14 @@ public class AddTableByColumnTaskTest extends BaseTest { getMigrator().migrate(); assertThat(JdbcUtils.getTableNames(getConnectionProperties()), containsInAnyOrder("FOO_TABLE", "TGT_TABLE")); + Set indexes = JdbcUtils.getIndexNames(getConnectionProperties(), "FOO_TABLE") + .stream() + .filter(s -> !s.startsWith("FK_REF_INDEX_")) + .filter(s -> !s.startsWith("PRIMARY_KEY_")) + .collect(Collectors.toSet()); + assertThat(indexes, containsInAnyOrder("IDX_HELLO")); } - private static class MyMigrationTasks extends BaseMigrationTasks { public MyMigrationTasks() { Builder v = forVersion(VersionEnum.V3_5_0); @@ -34,10 +40,13 @@ public class AddTableByColumnTaskTest extends BaseTest { Builder.BuilderAddTableByColumns fooTable = v.addTableByColumns("3", "FOO_TABLE", "PID"); fooTable.addColumn("PID").nonNullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.LONG); fooTable.addColumn("HELLO").nullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 200); + fooTable.addColumn("GOODBYE").nullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 200); fooTable.addColumn("COL_REF").nullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.LONG); fooTable.addIndex("4", "IDX_HELLO").unique(true).withColumns("HELLO"); - fooTable.addForeignKey("5", "FK_REF").toColumn("COL_REF").references("TGT_TABLE", "PID"); - + // FIXME KHS add doNothing() somewhere in this builder (find the sync this is using) + fooTable.addIndex("5", "IDX_GOODBYE").unique(true).withColumnsStub("GOODBYE"); + fooTable.dropIndexStub("6", "IDX_HELLO"); + fooTable.addForeignKey("7", "FK_REF").toColumn("COL_REF").references("TGT_TABLE", "PID"); } } } From b0b410384ff74d4f8ecd98212297443dc95dfc14 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Mon, 9 Dec 2019 15:42:40 -0500 Subject: [PATCH 09/13] add renameIndex task --- .../jpa/migrate/taskdef/RenameIndexTask.java | 142 ++++++++++++++++++ .../fhir/jpa/migrate/tasks/api/Builder.java | 8 + .../taskdef/AddTableByColumnTaskTest.java | 5 +- 3 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameIndexTask.java diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameIndexTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameIndexTask.java new file mode 100644 index 00000000000..d97de69f666 --- /dev/null +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameIndexTask.java @@ -0,0 +1,142 @@ +package ca.uhn.fhir.jpa.migrate.taskdef; + +/*- + * #%L + * HAPI FHIR JPA Server - Migration + * %% + * Copyright (C) 2014 - 2019 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; +import ca.uhn.fhir.jpa.migrate.JdbcUtils; +import org.apache.commons.lang3.Validate; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.intellij.lang.annotations.Language; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +public class RenameIndexTask extends BaseTableTask { + + private static final Logger ourLog = LoggerFactory.getLogger(RenameIndexTask.class); + private String myOldIndexName; + private String myNewIndexName; + + + public RenameIndexTask(String theProductVersion, String theSchemaVersion) { + super(theProductVersion, theSchemaVersion); + } + + @Override + public void validate() { + super.validate(); + Validate.notBlank(myOldIndexName, "The old index name must not be blank"); + Validate.notBlank(myNewIndexName, "The new index name must not be blank"); + + setDescription("Rename index from " + myOldIndexName + " to " + myNewIndexName + " on table " + getTableName()); + } + + @Override + public void doExecute() throws SQLException { + Set indexNames = JdbcUtils.getIndexNames(getConnectionProperties(), getTableName()); + + if (!indexNames.contains(myOldIndexName)) { + logInfo(ourLog, "Index {} does not exist on table {} - No action needed", myOldIndexName, getTableName()); + return; + } + + List sqls = createRenameIndexSql(getConnectionProperties(), getTableName(), myOldIndexName, myNewIndexName, getDriverType()); + if (!sqls.isEmpty()) { + logInfo(ourLog, "Renaming index from {} to {} on table {}", myOldIndexName, myNewIndexName, getTableName()); + } + for (@Language("SQL") String sql : sqls) { + executeSql(getTableName(), sql); + } + } + + public RenameIndexTask setNewIndexName(String theNewIndexName) { + myNewIndexName = theNewIndexName; + return this; + } + + public RenameIndexTask setOldIndexName(String theOldIndexName) { + myOldIndexName = theOldIndexName; + return this; + } + + static List createRenameIndexSql(DriverTypeEnum.ConnectionProperties theConnectionProperties, String theTableName, String theOldIndexName, String theNewIndexName, DriverTypeEnum theDriverType) throws SQLException { + Validate.notBlank(theOldIndexName, "theOldIndexName must not be blank"); + Validate.notBlank(theNewIndexName, "theNewIndexName must not be blank"); + Validate.notBlank(theTableName, "theTableName must not be blank"); + + if (!JdbcUtils.getIndexNames(theConnectionProperties, theTableName).contains(theOldIndexName)) { + return Collections.emptyList(); + } + + boolean isUnique = JdbcUtils.isIndexUnique(theConnectionProperties, theTableName, theOldIndexName); + + List sql = new ArrayList<>(); + + // Drop constraint + switch (theDriverType) { + case MYSQL_5_7: + case MARIADB_10_1: + case DERBY_EMBEDDED: + sql.add("rename index " + theOldIndexName + " to " + theNewIndexName); + break; + case H2_EMBEDDED: + case POSTGRES_9_4: + case ORACLE_12C: + sql.add("alter index " + theOldIndexName + " rename to " + theNewIndexName); + break; + case MSSQL_2012: + // FIXME KHS really? + sql.add("EXEC sp_rename '" + theOldIndexName + "', '" + theNewIndexName + "'; GO"); + break; + } + return sql; + } + + @Override + public boolean equals(Object theO) { + if (this == theO) return true; + + if (theO == null || getClass() != theO.getClass()) return false; + + RenameIndexTask that = (RenameIndexTask) theO; + + return new EqualsBuilder() + .appendSuper(super.equals(theO)) + .append(myOldIndexName, that.myOldIndexName) + .append(myNewIndexName, that.myNewIndexName) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .appendSuper(super.hashCode()) + .append(myOldIndexName) + .append(myNewIndexName) + .toHashCode(); + } +} diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java index 6ae87bd732c..98daf371c57 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java @@ -162,6 +162,14 @@ public class Builder { addTask(task); } + public void renameIndex(String theVersion, String theOldIndexName, String theNewIndexName) { + RenameIndexTask task = new RenameIndexTask(myRelease, theVersion); + task.setOldIndexName(theOldIndexName); + task.setNewIndexName(theNewIndexName); + task.setTableName(myTableName); + addTask(task); + } + public void dropThisTable(String theVersion) { DropTableTask task = new DropTableTask(myRelease, theVersion); task.setTableName(myTableName); diff --git a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java index 8b5f91596ab..d4088a0be04 100644 --- a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java +++ b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java @@ -27,7 +27,7 @@ public class AddTableByColumnTaskTest extends BaseTest { .filter(s -> !s.startsWith("FK_REF_INDEX_")) .filter(s -> !s.startsWith("PRIMARY_KEY_")) .collect(Collectors.toSet()); - assertThat(indexes, containsInAnyOrder("IDX_HELLO")); + assertThat(indexes, containsInAnyOrder("IDX_BONJOUR")); } private static class MyMigrationTasks extends BaseMigrationTasks { @@ -47,6 +47,9 @@ public class AddTableByColumnTaskTest extends BaseTest { fooTable.addIndex("5", "IDX_GOODBYE").unique(true).withColumnsStub("GOODBYE"); fooTable.dropIndexStub("6", "IDX_HELLO"); fooTable.addForeignKey("7", "FK_REF").toColumn("COL_REF").references("TGT_TABLE", "PID"); + + Builder.BuilderWithTableName renameIndexTable = v.onTable("FOO_TABLE"); + renameIndexTable.renameIndex("8", "IDX_HELLO", "IDX_BONJOUR"); } } } From 77373605bd8afc85614b53c116089e1cdb7b60fd Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Mon, 9 Dec 2019 15:58:08 -0500 Subject: [PATCH 10/13] add 420 migration --- .../jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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 38f30caf23c..542931d9c33 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 @@ -55,10 +55,15 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { init350(); // 20180601 - 20180917 init360(); // 20180918 - 20181112 init400(); // 20190401 - 20190814 - init410(); // 20190815 - present + init410(); // 20190815 - 20191014 + init420(); // 20191015 - present } - protected void init410() { // 20190815 - present + protected void init420() { // 20191015 - present + Builder version = forVersion(VersionEnum.V4_2_0); + } + + protected void init410() { // 20190815 - 20191014 Builder version = forVersion(VersionEnum.V4_1_0); /* From bd1b41ff82eeed3e1472b47b48d6d4b0da5a3cab Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Mon, 9 Dec 2019 16:44:24 -0500 Subject: [PATCH 11/13] change out-of-order-permitted from environment variablet to flag to work like dont-use-flyway --- .../fhir/cli/BaseFlywayMigrateDatabaseCommand.java | 12 ++++++++---- .../ca/uhn/hapi/fhir/docs/server_jpa/upgrading.md | 8 ++------ .../java/ca/uhn/fhir/jpa/migrate/BaseMigrator.java | 8 ++++++++ .../java/ca/uhn/fhir/jpa/migrate/FlywayMigrator.java | 6 +----- .../java/ca/uhn/fhir/jpa/migrate/SchemaMigrator.java | 6 ++++++ .../ca/uhn/fhir/jpa/migrate/SchemaMigratorTest.java | 2 +- 6 files changed, 26 insertions(+), 16 deletions(-) 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 06e99e469f2..ed63bc26c0b 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 @@ -24,7 +24,6 @@ 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; @@ -45,7 +44,9 @@ public abstract class BaseFlywayMigrateDatabaseCommand extends B public static final String MIGRATE_DATABASE = "migrate-database"; + public static final String NO_COLUMN_SHRINK = "no-column-shrink"; public static final String DONT_USE_FLYWAY = "dont-use-flyway"; + public static final String OUT_OF_ORDER_PERMITTED = "out-of-order-permitted"; private Set myFlags; private String myMigrationTableName; @@ -86,7 +87,8 @@ public abstract class BaseFlywayMigrateDatabaseCommand extends B 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."); + addOptionalOption(retVal, null, OUT_OF_ORDER_PERMITTED,false, "If this option is set, the migrator will permit migration tasks to be run out of order. It shouldn't be required in most cases, however may be the solution if you see the error message 'Detected resolved migration not applied to database'."); + 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 +112,7 @@ public abstract class BaseFlywayMigrateDatabaseCommand extends B } boolean dryRun = theCommandLine.hasOption("r"); - boolean noColumnShrink = theCommandLine.hasOption("no-column-shrink"); + boolean noColumnShrink = theCommandLine.hasOption(BaseFlywayMigrateDatabaseCommand.NO_COLUMN_SHRINK); String flags = theCommandLine.getOptionValue("x"); myFlags = Arrays.stream(defaultString(flags).split(",")) @@ -118,7 +120,8 @@ public abstract class BaseFlywayMigrateDatabaseCommand extends B .filter(StringUtils::isNotBlank) .collect(Collectors.toSet()); - boolean dontUseFlyway = theCommandLine.hasOption("dont-use-flyway"); + boolean dontUseFlyway = theCommandLine.hasOption(BaseFlywayMigrateDatabaseCommand.DONT_USE_FLYWAY); + boolean outOfOrderPermitted = theCommandLine.hasOption(BaseFlywayMigrateDatabaseCommand.OUT_OF_ORDER_PERMITTED); BaseMigrator migrator; if (dontUseFlyway) { @@ -132,6 +135,7 @@ public abstract class BaseFlywayMigrateDatabaseCommand extends B migrator.setPassword(password); migrator.setDryRun(dryRun); migrator.setNoColumnShrink(noColumnShrink); + migrator.setOutOfOrderPermitted(outOfOrderPermitted); addTasks(migrator); migrator.migrate(); } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/upgrading.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/upgrading.md index 5a1c476ba62..c2fe16d54e0 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/upgrading.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/upgrading.md @@ -9,7 +9,7 @@ Note that this feature was added in HAPI FHIR 3.5.0. It is not able to migrate f The following example shows how to use the migrator utility to migrate to the latest version. ```bash -./hapi-fhir-cli migrate-database -d H2_EMBEDDED -u "jdbc:derby:directory:target/jpaserver_derby_files;create=true" -n "" -p "" +./hapi-fhir-cli migrate-database -d H2_EMBEDDED -u "jdbc:h2:directory:target/jpaserver_h2_files;create=true" -n "" -p "" ``` You may use the following command to get detailed help on the options: @@ -64,14 +64,10 @@ SELECT * FROM HFJ_RES_REINDEX_JOB * Execute the migrator tool again, this time omitting the flag option, e.g. ```bash -./hapi-fhir-cli migrate-database -d DERBY_EMBEDDED -u "jdbc:h2:directory:target/jpaserver_h2_files;create=true" -n "" -p "" +./hapi-fhir-cli migrate-database -d H2_EMBEDDED -u "jdbc:h2:directory:target/jpaserver_h2_files;create=true" -n "" -p "" ``` * Rebuild, and start HAPI FHIR JPA again. # Flyway As of version 4.2.0, HAPI FHIR JPA now uses Flyway for schema migrations. The "from" and "to" parameters are no longer used. Flyway maintains a list of completed migrations in a table called `FLY_HFJ_MIGRATION`. When you run the migration command, flyway scans the list of completed migrations in this table and compares them to the list of known migrations, and runs only the new ones. - -## Recovering from a failed migration - -Under certain unlikely scenarios, you may see the error message "Detected resolved migration not applied to database". This means that Flyway has detected a new migration task that has an earlier version than the latest version in the Flyway table. You can set the environment variable `OUT_OF_ORDER_MIGRATION` to allow Flyway to run migration tasks out of order. 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 index 75e083894d9..b8e2c68fcad 100644 --- 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 @@ -30,6 +30,7 @@ public abstract class BaseMigrator { private boolean myDryRun; private boolean myNoColumnShrink; + private boolean myOutOfOrderPermitted; private DriverTypeEnum myDriverType; private String myConnectionUrl; private String myUsername; @@ -89,4 +90,11 @@ public abstract class BaseMigrator { myPassword = thePassword; } + public boolean isOutOfOrderPermitted() { + return myOutOfOrderPermitted; + } + + public void setOutOfOrderPermitted(boolean theOutOfOrderPermitted) { + myOutOfOrderPermitted = theOutOfOrderPermitted; + } } 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 947bef342b6..d9d560f3ea3 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 @@ -33,12 +33,9 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; -import static org.apache.commons.lang3.StringUtils.isNotBlank; - public class FlywayMigrator extends BaseMigrator { private static final Logger ourLog = LoggerFactory.getLogger(FlywayMigrator.class); - public static final String OUT_OF_ORDER_MIGRATION = "OUT_OF_ORDER_MIGRATION"; private final String myMigrationTableName; private List myTasks = new ArrayList<>(); @@ -82,12 +79,11 @@ public class FlywayMigrator extends BaseMigrator { private Flyway initFlyway(DriverTypeEnum.ConnectionProperties theConnectionProperties) { // TODO KHS Is there a way we can use datasource instead of url, username, password here - boolean outOfOrder = isNotBlank(System.getProperty(OUT_OF_ORDER_MIGRATION)); Flyway flyway = Flyway.configure() .table(myMigrationTableName) .dataSource(getConnectionUrl(), getUsername(), getPassword()) .baselineOnMigrate(true) - .outOfOrder(outOfOrder) + .outOfOrder(isOutOfOrderPermitted()) .javaMigrations(myTasks.toArray(new JavaMigration[0])) .load(); for (FlywayMigration task : myTasks) { 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 deab31c0661..c4faed34720 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 @@ -43,6 +43,7 @@ public class SchemaMigrator { private final String myMigrationTableName; private final List> myMigrationTasks; private boolean myDontUseFlyway; + private boolean myOutOfOrderPermitted; private DriverTypeEnum myDriverType; /** @@ -64,6 +65,10 @@ public class SchemaMigrator { myDontUseFlyway = theDontUseFlyway; } + public void setOutOfOrderPermitted(boolean theOutOfOrderPermitted) { + myOutOfOrderPermitted = theOutOfOrderPermitted; + } + public void validate() { if (mySkipValidation) { ourLog.warn("Database running in hibernate auto-update mode. Skipping schema validation."); @@ -102,6 +107,7 @@ public class SchemaMigrator { migrator.setPassword(myDataSource.getPassword()); } else { migrator = new FlywayMigrator(myMigrationTableName, myDataSource); + migrator.setOutOfOrderPermitted(myOutOfOrderPermitted); } migrator.addTasks(myMigrationTasks); return migrator; 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 d778c511baa..924cbdc5975 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 @@ -62,7 +62,7 @@ public class SchemaMigratorTest extends BaseTest { } catch (FlywayException e) { assertThat(e.getMessage(), containsString("Detected resolved migration not applied to database: 1.1")); } - System.setProperty(FlywayMigrator.OUT_OF_ORDER_MIGRATION, "true"); + schemaMigrator.setOutOfOrderPermitted(true); schemaMigrator.migrate(); } From 3657a3ad5a399c3b501b39ec386be226271c986c Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Mon, 9 Dec 2019 16:51:42 -0500 Subject: [PATCH 12/13] pre-review cleanup --- .../java/ca/uhn/fhir/jpa/migrate/taskdef/RenameIndexTask.java | 2 -- .../main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java | 2 -- .../uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java | 1 - 3 files changed, 5 deletions(-) diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameIndexTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameIndexTask.java index d97de69f666..6d80d9fa3c2 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameIndexTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameIndexTask.java @@ -36,12 +36,10 @@ import java.util.List; import java.util.Set; public class RenameIndexTask extends BaseTableTask { - private static final Logger ourLog = LoggerFactory.getLogger(RenameIndexTask.class); private String myOldIndexName; private String myNewIndexName; - public RenameIndexTask(String theProductVersion, String theSchemaVersion) { super(theProductVersion, theSchemaVersion); } diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java index 98daf371c57..18248f2d6fd 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java @@ -381,7 +381,6 @@ public class Builder { private final String myVersion; private final String myColumnName; private final BaseMigrationTasks.IAcceptsTasks myTaskSink; - private boolean myDoNothing; public BuilderAddColumnWithName(String theRelease, String theVersion, String theColumnName, BaseMigrationTasks.IAcceptsTasks theTaskSink) { myRelease = theRelease; @@ -423,7 +422,6 @@ public class Builder { } myTaskSink.addTask(task); } - } } } diff --git a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java index d4088a0be04..6ecb34dcc2a 100644 --- a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java +++ b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java @@ -43,7 +43,6 @@ public class AddTableByColumnTaskTest extends BaseTest { fooTable.addColumn("GOODBYE").nullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 200); fooTable.addColumn("COL_REF").nullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.LONG); fooTable.addIndex("4", "IDX_HELLO").unique(true).withColumns("HELLO"); - // FIXME KHS add doNothing() somewhere in this builder (find the sync this is using) fooTable.addIndex("5", "IDX_GOODBYE").unique(true).withColumnsStub("GOODBYE"); fooTable.dropIndexStub("6", "IDX_HELLO"); fooTable.addForeignKey("7", "FK_REF").toColumn("COL_REF").references("TGT_TABLE", "PID"); From 198779a25f4d0c327b794ac08611e61548bf2639 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Mon, 9 Dec 2019 18:36:41 -0500 Subject: [PATCH 13/13] fix MSSQL index rename --- .../ca/uhn/fhir/jpa/migrate/taskdef/RenameIndexTask.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameIndexTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameIndexTask.java index 6d80d9fa3c2..ad6852fa3ad 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameIndexTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameIndexTask.java @@ -90,8 +90,6 @@ public class RenameIndexTask extends BaseTableTask { return Collections.emptyList(); } - boolean isUnique = JdbcUtils.isIndexUnique(theConnectionProperties, theTableName, theOldIndexName); - List sql = new ArrayList<>(); // Drop constraint @@ -107,8 +105,7 @@ public class RenameIndexTask extends BaseTableTask { sql.add("alter index " + theOldIndexName + " rename to " + theNewIndexName); break; case MSSQL_2012: - // FIXME KHS really? - sql.add("EXEC sp_rename '" + theOldIndexName + "', '" + theNewIndexName + "'; GO"); + sql.add("EXEC sp_rename '" + theTableName + "." + theOldIndexName + "', '" + theNewIndexName + "'"); break; } return sql;