Added tests for cases where a renamed column is used in a Foreign Key constraint.

This commit is contained in:
ianmarshall 2020-06-17 11:56:20 -04:00
parent eb990e9a68
commit 5d22af45bf
2 changed files with 126 additions and 2 deletions

View File

@ -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<String> 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;
}
}

View File

@ -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)");