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 57aee17fe56..7e40827ffa1 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 @@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.migrate.taskdef; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; import ca.uhn.fhir.jpa.migrate.JdbcUtils; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.slf4j.Logger; @@ -42,6 +43,8 @@ public class RenameColumnTask extends BaseTableTask { private boolean myIsOkayIfNeitherColumnExists; private boolean myDeleteTargetColumnFirstIfBothExist; + private boolean mySimulateMySQLForTest = false; + public RenameColumnTask(String theProductVersion, String theSchemaVersion) { super(theProductVersion, theSchemaVersion); } @@ -84,8 +87,8 @@ public class RenameColumnTask extends BaseTableTask { throw new SQLException("Can not rename " + getTableName() + "." + myOldName + " to " + myNewName + " because both columns exist and data exists in " + myNewName); } - if (getDriverType().equals(DriverTypeEnum.MYSQL_5_7)) { - // Some DBs such as MYSQL require that foreign keys depending on the column be dropped before the column itself is dropped. + if (getDriverType().equals(DriverTypeEnum.MYSQL_5_7) || mySimulateMySQLForTest) { + // Some DBs such as MYSQL require that foreign keys depending on the column be explicitly dropped before the column itself is dropped. logInfo(ourLog, "Table {} has columns {} and {} - Going to drop any foreign keys depending on column {} before renaming", getTableName(), myOldName, myNewName, myNewName); Set foreignKeys = JdbcUtils.getForeignKeysForColumn(getConnectionProperties(), myNewName, getTableName()); if(foreignKeys != null) { @@ -173,4 +176,9 @@ public class RenameColumnTask extends BaseTableTask { theBuilder.append(myOldName); theBuilder.append(myNewName); } + + @VisibleForTesting + void setSimulateMySQLForTest(boolean theSimulateMySQLForTest) { + mySimulateMySQLForTest = theSimulateMySQLForTest; + } } diff --git a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameColumnTaskTest.java b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameColumnTaskTest.java index f1927501f69..f4f2dddf4f4 100644 --- a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameColumnTaskTest.java +++ b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameColumnTaskTest.java @@ -8,7 +8,10 @@ import java.sql.SQLException; import java.util.Set; import java.util.function.Supplier; +import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; @@ -35,6 +38,41 @@ public class RenameColumnTaskTest extends BaseTest { assertThat(JdbcUtils.getColumnNames(getConnectionProperties(), "SOMETABLE"), containsInAnyOrder("PID", "TEXTCOL")); } + @Test + public void testForeignKeyColumnAlreadyExists_MySql() throws SQLException { + testForeignKeyColumnAlreadyExists(true); + } + + private void testForeignKeyColumnAlreadyExists(boolean isMySql) throws SQLException { + executeSql("create table PARENT (PID bigint not null, TEXTCOL varchar(255), primary key (PID))"); + executeSql("create table CHILD (PID bigint not null, PARENTREF bigint)"); + executeSql("alter table CHILD add constraint FK_MOM foreign key (PARENTREF) references PARENT(PID)"); + + assertThat(JdbcUtils.getForeignKeys(getConnectionProperties(), "PARENT", "CHILD"), hasSize(1)); + + assertThat(JdbcUtils.getForeignKeysForColumn(getConnectionProperties(), "PARENTREF", "CHILD"), containsInAnyOrder("FK_MOM")); + + RenameColumnTask task = new RenameColumnTask("1", "1"); + task.setTableName("CHILD"); + task.setOldName("myParentRef"); + task.setNewName("PARENTREF"); + task.setSimulateMySQLForTest(isMySql); + getMigrator().addTask(task); + + getMigrator().migrate(); + + assertThat(JdbcUtils.getForeignKeys(getConnectionProperties(), "PARENT", "CHILD"), hasSize(1)); + + assertThat(JdbcUtils.getColumnNames(getConnectionProperties(), "CHILD"), containsInAnyOrder("PID", "PARENTREF")); + + assertThat(JdbcUtils.getForeignKeysForColumn(getConnectionProperties(), "PARENTREF", "CHILD"), containsInAnyOrder("FK_MOM")); + } + + @Test + public void testForeignKeyColumnAlreadyExists_OtherDB() throws SQLException { + testForeignKeyColumnAlreadyExists(false); + } + @Test public void testBothExistDeleteTargetFirst() throws SQLException { executeSql("create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255), myTextCol varchar(255))"); @@ -53,6 +91,48 @@ public class RenameColumnTaskTest extends BaseTest { assertThat(columnNames.toString(), columnNames, containsInAnyOrder("PID", "TEXTCOL")); } + @Test + public void testForeignKeyColumnBothExistDeleteTargetFirst_MySql() throws SQLException { + testForeignKeyColumnBothExistDeleteTargetFirst(true); + } + + private void testForeignKeyColumnBothExistDeleteTargetFirst(boolean isMySql) throws SQLException { + executeSql("create table PARENT (PARENTID bigint not null, TEXTCOL varchar(255), primary key (PARENTID))"); + executeSql("create table RELATION (RELATIONID bigint not null, TEXTCOL varchar(255), primary key (RELATIONID))"); + executeSql("create table CHILD (PID bigint not null, PARENTREF bigint, NOKREF bigint)"); + executeSql("alter table CHILD add constraint FK_MOM foreign key (PARENTREF) references PARENT(PARENTID)"); + executeSql("alter table CHILD add constraint FK_NOK foreign key (NOKREF) references RELATION(RELATIONID)"); + + assertThat(JdbcUtils.getForeignKeys(getConnectionProperties(), "PARENT", "CHILD"), hasSize(1)); + assertThat(JdbcUtils.getForeignKeys(getConnectionProperties(), "RELATION", "CHILD"), hasSize(1)); + + assertThat(JdbcUtils.getForeignKeysForColumn(getConnectionProperties(), "PARENTREF", "CHILD"), containsInAnyOrder("FK_MOM")); + assertThat(JdbcUtils.getForeignKeysForColumn(getConnectionProperties(), "NOKREF", "CHILD"), containsInAnyOrder("FK_NOK")); + + RenameColumnTask task = new RenameColumnTask("1", "1"); + task.setTableName("CHILD"); + task.setOldName("PARENTREF"); + task.setNewName("NOKREF"); + task.setDeleteTargetColumnFirstIfBothExist(true); + task.setSimulateMySQLForTest(isMySql); + getMigrator().addTask(task); + + getMigrator().migrate(); + + assertThat(JdbcUtils.getForeignKeys(getConnectionProperties(), "RELATION", "CHILD"), empty()); + assertThat(JdbcUtils.getForeignKeys(getConnectionProperties(), "PARENT", "CHILD"), hasSize(1)); + + assertThat(JdbcUtils.getColumnNames(getConnectionProperties(), "CHILD"), containsInAnyOrder("PID", "NOKREF")); + + assertThat(JdbcUtils.getForeignKeysForColumn(getConnectionProperties(), "NOKREF", "CHILD"), containsInAnyOrder("FK_MOM")); + + } + + @Test + public void testForeignKeyColumnBothExistDeleteTargetFirst_OtherDB() throws SQLException { + testForeignKeyColumnBothExistDeleteTargetFirst(false); + } + @Test public void testBothExistDeleteTargetFirstDataExistsInSourceAndTarget() throws SQLException { executeSql("create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255), myTextCol varchar(255))"); @@ -91,6 +171,42 @@ public class RenameColumnTaskTest extends BaseTest { assertThat(JdbcUtils.getColumnNames(getConnectionProperties(), "SOMETABLE"), containsInAnyOrder("PID", "TEXTCOL")); } + @Test + public void testForeignKeyColumnDoesntAlreadyExist_MySql() throws SQLException { + testForeignKeyColumnDoesntAlreadyExist(true); + } + + private void testForeignKeyColumnDoesntAlreadyExist(boolean isMySql) throws SQLException { + executeSql("create table PARENT (PARENTID bigint not null, TEXTCOL varchar(255), primary key (PARENTID))"); + executeSql("create table CHILD (PID bigint not null, PARENTREF bigint)"); + executeSql("alter table CHILD add constraint FK_MOM foreign key (PARENTREF) references PARENT(PARENTID)"); + + assertThat(JdbcUtils.getForeignKeys(getConnectionProperties(), "PARENT", "CHILD"), hasSize(1)); + + assertThat(JdbcUtils.getForeignKeysForColumn(getConnectionProperties(), "PARENTREF", "CHILD"), containsInAnyOrder("FK_MOM")); + + RenameColumnTask task = new RenameColumnTask("1", "1"); + task.setTableName("CHILD"); + task.setOldName("PARENTREF"); + task.setNewName("MOMREF"); + task.setSimulateMySQLForTest(isMySql); + getMigrator().addTask(task); + + getMigrator().migrate(); + + assertThat(JdbcUtils.getForeignKeys(getConnectionProperties(), "PARENT", "CHILD"), hasSize(1)); + + assertThat(JdbcUtils.getColumnNames(getConnectionProperties(), "CHILD"), containsInAnyOrder("PID", "MOMREF")); + + assertThat(JdbcUtils.getForeignKeysForColumn(getConnectionProperties(), "MOMREF", "CHILD"), containsInAnyOrder("FK_MOM")); + + } + + @Test + public void testForeignKeyColumnDoesntAlreadyExist_OtherDB() throws SQLException { + testForeignKeyColumnDoesntAlreadyExist(false); + } + @Test public void testNeitherColumnExists() { executeSql("create table SOMETABLE (PID bigint not null)");