stub out all migrations if an empty schema was initialized (#1975)

This commit is contained in:
Ken Stevens 2020-07-09 20:18:54 -04:00 committed by GitHub
parent 1449b9cb0a
commit 50ae07acaa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 184 additions and 11 deletions

View File

@ -35,6 +35,7 @@ public abstract class BaseMigrator implements IMigrator {
private boolean myDryRun; private boolean myDryRun;
private boolean myNoColumnShrink; private boolean myNoColumnShrink;
private boolean myOutOfOrderPermitted; private boolean myOutOfOrderPermitted;
private boolean mySchemaWasInitialized;
private DriverTypeEnum myDriverType; private DriverTypeEnum myDriverType;
private DataSource myDataSource; private DataSource myDataSource;
private List<BaseTask.ExecutedStatement> myExecutedStatements = new ArrayList<>(); private List<BaseTask.ExecutedStatement> myExecutedStatements = new ArrayList<>();
@ -111,4 +112,13 @@ public abstract class BaseMigrator implements IMigrator {
} }
return statementBuilder; return statementBuilder;
} }
public boolean isSchemaWasInitialized() {
return mySchemaWasInitialized;
}
public BaseMigrator setSchemaWasInitialized(boolean theSchemaWasInitialized) {
mySchemaWasInitialized = theSchemaWasInitialized;
return this;
}
} }

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.migrate;
*/ */
import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask; import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask;
import ca.uhn.fhir.jpa.migrate.taskdef.InitializeSchemaTask;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.flywaydb.core.api.MigrationVersion; import org.flywaydb.core.api.MigrationVersion;
import org.flywaydb.core.api.migration.Context; import org.flywaydb.core.api.migration.Context;
@ -32,14 +33,14 @@ import java.sql.SQLException;
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
public class FlywayMigration implements JavaMigration { public class FlywayMigrationTask implements JavaMigration {
private static final Logger ourLog = LoggerFactory.getLogger(FlywayMigration.class); private static final Logger ourLog = LoggerFactory.getLogger(FlywayMigrationTask.class);
private final BaseTask myTask; private final BaseTask myTask;
private final FlywayMigrator myFlywayMigrator; private final FlywayMigrator myFlywayMigrator;
private DriverTypeEnum.ConnectionProperties myConnectionProperties; private DriverTypeEnum.ConnectionProperties myConnectionProperties;
public FlywayMigration(BaseTask theTask, FlywayMigrator theFlywayMigrator) { public FlywayMigrationTask(BaseTask theTask, FlywayMigrator theFlywayMigrator) {
myTask = theTask; myTask = theTask;
myFlywayMigrator = theFlywayMigrator; myFlywayMigrator = theFlywayMigrator;
} }
@ -76,8 +77,7 @@ public class FlywayMigration implements JavaMigration {
myTask.setNoColumnShrink(myFlywayMigrator.isNoColumnShrink()); myTask.setNoColumnShrink(myFlywayMigrator.isNoColumnShrink());
myTask.setConnectionProperties(myConnectionProperties); myTask.setConnectionProperties(myConnectionProperties);
try { try {
myTask.execute(); executeTask();
myFlywayMigrator.addExecutedStatements(myTask.getExecutedStatements());
} catch (SQLException e) { } catch (SQLException e) {
String description = myTask.getDescription(); String description = myTask.getDescription();
if (isBlank(description)) { if (isBlank(description)) {
@ -88,6 +88,20 @@ public class FlywayMigration implements JavaMigration {
} }
} }
private void executeTask() throws SQLException {
if (myFlywayMigrator.isSchemaWasInitialized() && !(myTask instanceof InitializeSchemaTask)) {
// Empty schema was initialized, stub out this non-schema-init task since we're starting with a fully migrated schema
myTask.setDoNothing(true);
}
myTask.execute();
if (myTask.initializedSchema()) {
ourLog.info("Empty schema was Initialized. Stubbing out all following migration tasks that are not Schema Initializations.");
myFlywayMigrator.setSchemaWasInitialized(true);
}
myFlywayMigrator.addExecutedStatements(myTask.getExecutedStatements());
}
public void setConnectionProperties(DriverTypeEnum.ConnectionProperties theConnectionProperties) { public void setConnectionProperties(DriverTypeEnum.ConnectionProperties theConnectionProperties) {
myConnectionProperties = theConnectionProperties; myConnectionProperties = theConnectionProperties;
} }

View File

@ -40,7 +40,7 @@ public class FlywayMigrator extends BaseMigrator {
private static final Logger ourLog = LoggerFactory.getLogger(FlywayMigrator.class); private static final Logger ourLog = LoggerFactory.getLogger(FlywayMigrator.class);
private final String myMigrationTableName; private final String myMigrationTableName;
private List<FlywayMigration> myTasks = new ArrayList<>(); private List<FlywayMigrationTask> myTasks = new ArrayList<>();
public FlywayMigrator(String theMigrationTableName, DataSource theDataSource, DriverTypeEnum theDriverType) { public FlywayMigrator(String theMigrationTableName, DataSource theDataSource, DriverTypeEnum theDriverType) {
this(theMigrationTableName); this(theMigrationTableName);
@ -53,7 +53,7 @@ public class FlywayMigrator extends BaseMigrator {
} }
public void addTask(BaseTask theTask) { public void addTask(BaseTask theTask) {
myTasks.add(new FlywayMigration(theTask, this)); myTasks.add(new FlywayMigrationTask(theTask, this));
} }
@Override @Override
@ -80,7 +80,7 @@ public class FlywayMigrator extends BaseMigrator {
.javaMigrations(myTasks.toArray(new JavaMigration[0])) .javaMigrations(myTasks.toArray(new JavaMigration[0]))
.callbacks(getCallbacks().toArray(new Callback[0])) .callbacks(getCallbacks().toArray(new Callback[0]))
.load(); .load();
for (FlywayMigration task : myTasks) { for (FlywayMigrationTask task : myTasks) {
task.setConnectionProperties(theConnectionProperties); task.setConnectionProperties(theConnectionProperties);
} }
return flyway; return flyway;

View File

@ -230,6 +230,10 @@ public abstract class BaseTask {
protected abstract void generateEquals(EqualsBuilder theBuilder, BaseTask theOtherObject); protected abstract void generateEquals(EqualsBuilder theBuilder, BaseTask theOtherObject);
public boolean initializedSchema() {
return false;
}
public static class ExecutedStatement { public static class ExecutedStatement {
private final String mySql; private final String mySql;
private final List<Object> myArguments; private final List<Object> myArguments;

View File

@ -37,6 +37,7 @@ public class InitializeSchemaTask extends BaseTask {
public static final String DESCRIPTION_PREFIX = "Initialize schema for "; public static final String DESCRIPTION_PREFIX = "Initialize schema for ";
private final ISchemaInitializationProvider mySchemaInitializationProvider; private final ISchemaInitializationProvider mySchemaInitializationProvider;
private boolean myInitializedSchema;
public InitializeSchemaTask(String theProductVersion, String theSchemaVersion, ISchemaInitializationProvider theSchemaInitializationProvider) { public InitializeSchemaTask(String theProductVersion, String theSchemaVersion, ISchemaInitializationProvider theSchemaInitializationProvider) {
super(theProductVersion, theSchemaVersion); super(theProductVersion, theSchemaVersion);
@ -68,9 +69,18 @@ public class InitializeSchemaTask extends BaseTask {
executeSql(null, nextSql); executeSql(null, nextSql);
} }
if (mySchemaInitializationProvider.canInitializeSchema()) {
myInitializedSchema = true;
}
logInfo(ourLog, "{} schema for {} initialized successfully", driverType, mySchemaInitializationProvider.getSchemaDescription()); logInfo(ourLog, "{} schema for {} initialized successfully", driverType, mySchemaInitializationProvider.getSchemaDescription());
} }
@Override
public boolean initializedSchema() {
return myInitializedSchema;
}
@Override @Override
protected void generateEquals(EqualsBuilder theBuilder, BaseTask theOtherObject) { protected void generateEquals(EqualsBuilder theBuilder, BaseTask theOtherObject) {
InitializeSchemaTask otherObject = (InitializeSchemaTask) theOtherObject; InitializeSchemaTask otherObject = (InitializeSchemaTask) theOtherObject;

View File

@ -1139,7 +1139,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
protected void init330() { // 20180114 - 20180329 protected void init330() { // 20180114 - 20180329
Builder version = forVersion(VersionEnum.V3_3_0); Builder version = forVersion(VersionEnum.V3_3_0);
version.initializeSchema("20180115.0", new SchemaInitializationProvider("HAPI FHIR", "/ca/uhn/hapi/fhir/jpa/docs/database", "HFJ_RESOURCE")); version.initializeSchema("20180115.0", new SchemaInitializationProvider("HAPI FHIR", "/ca/uhn/hapi/fhir/jpa/docs/database", "HFJ_RESOURCE", true));
Builder.BuilderWithTableName hfjResource = version.onTable("HFJ_RESOURCE"); Builder.BuilderWithTableName hfjResource = version.onTable("HFJ_RESOURCE");
version.startSectionWithMessage("Starting work on table: " + hfjResource.getTableName()); version.startSectionWithMessage("Starting work on table: " + hfjResource.getTableName());

View File

@ -41,15 +41,18 @@ public class SchemaInitializationProvider implements ISchemaInitializationProvid
private String mySchemaDescription; private String mySchemaDescription;
private final String mySchemaExistsIndicatorTable; private final String mySchemaExistsIndicatorTable;
private final boolean myCanInitializeSchema;
/** /**
* @param theSchemaFileClassPath pathname to script used to initialize schema * @param theSchemaFileClassPath pathname to script used to initialize schema
* @param theSchemaExistsIndicatorTable a table name we can use to determine if this schema has already been initialized * @param theSchemaExistsIndicatorTable a table name we can use to determine if this schema has already been initialized
* @param theCanInitializeSchema this is a "root" schema initializer that creates the primary tables used by this app
*/ */
public SchemaInitializationProvider(String theSchemaDescription, String theSchemaFileClassPath, String theSchemaExistsIndicatorTable) { public SchemaInitializationProvider(String theSchemaDescription, String theSchemaFileClassPath, String theSchemaExistsIndicatorTable, boolean theCanInitializeSchema) {
mySchemaDescription = theSchemaDescription; mySchemaDescription = theSchemaDescription;
mySchemaFileClassPath = theSchemaFileClassPath; mySchemaFileClassPath = theSchemaFileClassPath;
mySchemaExistsIndicatorTable = theSchemaExistsIndicatorTable; mySchemaExistsIndicatorTable = theSchemaExistsIndicatorTable;
myCanInitializeSchema = theCanInitializeSchema;
} }
@Override @Override
@ -129,5 +132,10 @@ public class SchemaInitializationProvider implements ISchemaInitializationProvid
mySchemaDescription = theSchemaDescription; mySchemaDescription = theSchemaDescription;
return this; return this;
} }
@Override
public boolean canInitializeSchema() {
return myCanInitializeSchema;
}
} }

View File

@ -33,4 +33,6 @@ public interface ISchemaInitializationProvider {
String getSchemaDescription(); String getSchemaDescription();
ISchemaInitializationProvider setSchemaDescription(String theSchemaDescription); ISchemaInitializationProvider setSchemaDescription(String theSchemaDescription);
boolean canInitializeSchema();
} }

View File

@ -0,0 +1,120 @@
package ca.uhn.fhir.jpa.migrate;
import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask;
import ca.uhn.fhir.jpa.migrate.taskdef.InitializeSchemaTask;
import ca.uhn.fhir.jpa.migrate.tasks.api.ISchemaInitializationProvider;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.flywaydb.core.api.migration.Context;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.sql.SQLException;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class FlywayMigrationTaskTest {
@Mock
private FlywayMigrator myFlywayMigrator;
@Mock
private Context myContext;
TestTask myTestTask = new TestTask();
@Test
public void schemaInitializedStubsFollowingMigration() {
when(myFlywayMigrator.isSchemaWasInitialized()).thenReturn(true);
FlywayMigrationTask task = new FlywayMigrationTask(myTestTask, myFlywayMigrator);
task.migrate(myContext);
assertTrue(myTestTask.isDoNothing());
}
@Test
public void schemaNotInitializedStubsFollowingMigration() {
when(myFlywayMigrator.isSchemaWasInitialized()).thenReturn(false);
FlywayMigrationTask task = new FlywayMigrationTask(myTestTask, myFlywayMigrator);
task.migrate(myContext);
assertFalse(myTestTask.isDoNothing());
}
@Test
public void schemaInitializedStubsFollowingMigrationExceptInitSchemaTask() {
when(myFlywayMigrator.isSchemaWasInitialized()).thenReturn(true);
InitializeSchemaTask initSchemaTask = new TestInitializeSchemaTask(false);
FlywayMigrationTask task = new FlywayMigrationTask(initSchemaTask, myFlywayMigrator);
task.migrate(myContext);
assertFalse(myTestTask.isDoNothing());
}
@Test
public void schemaInitializedSetsInitializedFlag() {
InitializeSchemaTask initSchemaTask = new TestInitializeSchemaTask(true);
FlywayMigrationTask task = new FlywayMigrationTask(initSchemaTask, myFlywayMigrator);
task.migrate(myContext);
verify(myFlywayMigrator, times(1)).setSchemaWasInitialized(true);
}
@Test
public void nonInitSchemaInitializedSetsInitializedFlag() {
InitializeSchemaTask initSchemaTask = new TestInitializeSchemaTask(false);
FlywayMigrationTask task = new FlywayMigrationTask(initSchemaTask, myFlywayMigrator);
task.migrate(myContext);
verify(myFlywayMigrator, never()).setSchemaWasInitialized(true);
}
// Can't use @Mock since BaseTask.equals is final
private class TestTask extends BaseTask {
protected TestTask() {
super("1", "1");
}
@Override
public void validate() {
// do nothing
}
@Override
protected void doExecute() throws SQLException {
// do nothing
}
@Override
protected void generateHashCode(HashCodeBuilder theBuilder) {
// do nothing
}
@Override
protected void generateEquals(EqualsBuilder theBuilder, BaseTask theOtherObject) {
// do nothing
}
}
private class TestInitializeSchemaTask extends InitializeSchemaTask {
private final boolean myInitializedSchema;
public TestInitializeSchemaTask(boolean theInitializedSchema) {
super("1", "1", mock(ISchemaInitializationProvider.class));
myInitializedSchema = theInitializedSchema;
}
@Override
public void execute() throws SQLException {
// nothing
}
@Override
public boolean initializedSchema() {
return myInitializedSchema;
}
}
}

View File

@ -56,6 +56,11 @@ public class InitializeSchemaTaskTest extends BaseTest {
return this; return this;
} }
@Override
public boolean canInitializeSchema() {
return false;
}
@Override @Override
public boolean equals(Object theO) { public boolean equals(Object theO) {
if (this == theO) return true; if (this == theO) return true;