Merge remote-tracking branch 'remotes/origin/master' into expunge-resource-hook
This commit is contained in:
commit
3582fe4c8a
|
@ -24,6 +24,8 @@ import ca.uhn.fhir.jpa.migrate.JdbcUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.jdbc.core.ColumnMapRowMapper;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -57,6 +59,17 @@ public class RenameColumnTask extends BaseTableTask<RenameColumnTask> {
|
||||||
boolean haveNewName = columnNames.contains(myNewName.toUpperCase());
|
boolean haveNewName = columnNames.contains(myNewName.toUpperCase());
|
||||||
if (haveOldName && haveNewName) {
|
if (haveOldName && haveNewName) {
|
||||||
if (myDeleteTargetColumnFirstIfBothExist) {
|
if (myDeleteTargetColumnFirstIfBothExist) {
|
||||||
|
|
||||||
|
Integer rowsWithData = getConnectionProperties().getTxTemplate().execute(t -> {
|
||||||
|
String sql = "SELECT * FROM " + getTableName() + " WHERE " + myNewName + " IS NOT NULL";
|
||||||
|
JdbcTemplate jdbcTemplate = getConnectionProperties().newJdbcTemplate();
|
||||||
|
jdbcTemplate.setMaxRows(1);
|
||||||
|
return jdbcTemplate.query(sql, new ColumnMapRowMapper()).size();
|
||||||
|
});
|
||||||
|
if (rowsWithData > 0) {
|
||||||
|
throw new SQLException("Can not rename " + getTableName() + "." + myOldName + " to " + myNewName + " because both columns exist and data exists in " + myNewName);
|
||||||
|
}
|
||||||
|
|
||||||
ourLog.info("Table {} has columns {} and {} - Going to drop {} before renaming", getTableName(), myOldName, myNewName, myNewName);
|
ourLog.info("Table {} has columns {} and {} - Going to drop {} before renaming", getTableName(), myOldName, myNewName, myNewName);
|
||||||
String sql = DropColumnTask.createSql(getTableName(), myNewName);
|
String sql = DropColumnTask.createSql(getTableName(), myNewName);
|
||||||
executeSql(getTableName(), sql);
|
executeSql(getTableName(), sql);
|
||||||
|
|
|
@ -117,6 +117,53 @@ public class BaseMigrationTasks<T extends Enum> {
|
||||||
addTask(task);
|
addTask(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class BuilderAddTableRawSql {
|
||||||
|
|
||||||
|
private final AddTableRawSqlTask myTask;
|
||||||
|
|
||||||
|
protected BuilderAddTableRawSql(String theTableName) {
|
||||||
|
myTask = new AddTableRawSqlTask();
|
||||||
|
myTask.setTableName(theTableName);
|
||||||
|
addTask(myTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public BuilderAddTableRawSql addSql(DriverTypeEnum theDriverTypeEnum, @Language("SQL") String theSql) {
|
||||||
|
myTask.addSql(theDriverTypeEnum, theSql);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSql(@Language("SQL") String theSql) {
|
||||||
|
myTask.addSql(theSql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BuilderAddTableByColumns extends BuilderWithTableName implements IAcceptsTasks {
|
||||||
|
private final AddTableByColumnTask myTask;
|
||||||
|
|
||||||
|
public BuilderAddTableByColumns(IAcceptsTasks theSink, String theTableName, String thePkColumnName) {
|
||||||
|
super(theSink, theTableName);
|
||||||
|
myTask = new AddTableByColumnTask();
|
||||||
|
myTask.setTableName(theTableName);
|
||||||
|
myTask.setPkColumn(thePkColumnName);
|
||||||
|
theSink.addTask(myTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BuilderWithTableName.BuilderAddColumnWithName addColumn(String theColumnName) {
|
||||||
|
return new BuilderWithTableName.BuilderAddColumnWithName(theColumnName, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addTask(BaseTask<?> theTask) {
|
||||||
|
if (theTask instanceof AddColumnTask) {
|
||||||
|
myTask.addAddColumnTask((AddColumnTask) theTask);
|
||||||
|
} else {
|
||||||
|
super.addTask(theTask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class BuilderWithTableName implements IAcceptsTasks {
|
public static class BuilderWithTableName implements IAcceptsTasks {
|
||||||
private final String myTableName;
|
private final String myTableName;
|
||||||
private final IAcceptsTasks mySink;
|
private final IAcceptsTasks mySink;
|
||||||
|
@ -177,6 +224,12 @@ public class BaseMigrationTasks<T extends Enum> {
|
||||||
return renameColumn(theOldName, theNewName, false, false);
|
return renameColumn(theOldName, theNewName, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param theOldName The old column name
|
||||||
|
* @param theNewName The new column name
|
||||||
|
* @param theAllowNeitherColumnToExist Setting this to true means that it's not an error if neither column exists
|
||||||
|
* @param theDeleteTargetColumnFirstIfBothEixst Setting this to true causes the migrator to be ok with the target column existing. It will make sure that there is no data in the column with the new name, then delete it if so in order to make room for the renamed column. If there is data it will still bomb out.
|
||||||
|
*/
|
||||||
public BuilderWithTableName renameColumn(String theOldName, String theNewName, boolean theAllowNeitherColumnToExist, boolean theDeleteTargetColumnFirstIfBothEixst) {
|
public BuilderWithTableName renameColumn(String theOldName, String theNewName, boolean theAllowNeitherColumnToExist, boolean theDeleteTargetColumnFirstIfBothEixst) {
|
||||||
RenameColumnTask task = new RenameColumnTask();
|
RenameColumnTask task = new RenameColumnTask();
|
||||||
task.setTableName(myTableName);
|
task.setTableName(myTableName);
|
||||||
|
@ -217,47 +270,6 @@ public class BaseMigrationTasks<T extends Enum> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class BuilderAddColumnWithName {
|
|
||||||
private final String myColumnName;
|
|
||||||
private final IAcceptsTasks myTaskSink;
|
|
||||||
|
|
||||||
public BuilderAddColumnWithName(String theColumnName, IAcceptsTasks theTaskSink) {
|
|
||||||
myColumnName = theColumnName;
|
|
||||||
myTaskSink = theTaskSink;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BuilderAddColumnWithNameNullable nullable() {
|
|
||||||
return new BuilderAddColumnWithNameNullable(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BuilderAddColumnWithNameNullable nonNullable() {
|
|
||||||
return new BuilderAddColumnWithNameNullable(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BuilderAddColumnWithNameNullable {
|
|
||||||
private final boolean myNullable;
|
|
||||||
|
|
||||||
public BuilderAddColumnWithNameNullable(boolean theNullable) {
|
|
||||||
myNullable = theNullable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void type(AddColumnTask.ColumnTypeEnum theColumnType) {
|
|
||||||
type(theColumnType, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void type(AddColumnTask.ColumnTypeEnum theColumnType, Integer theLength) {
|
|
||||||
AddColumnTask task = new AddColumnTask();
|
|
||||||
task.setColumnName(myColumnName);
|
|
||||||
task.setNullable(myNullable);
|
|
||||||
task.setColumnType(theColumnType);
|
|
||||||
if (theLength != null) {
|
|
||||||
task.setColumnLength(theLength);
|
|
||||||
}
|
|
||||||
myTaskSink.addTask(task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BuilderModifyColumnWithName {
|
public class BuilderModifyColumnWithName {
|
||||||
|
|
||||||
private final String myColumnName;
|
private final String myColumnName;
|
||||||
|
@ -341,51 +353,45 @@ public class BaseMigrationTasks<T extends Enum> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class BuilderAddColumnWithName {
|
||||||
|
private final String myColumnName;
|
||||||
|
private final IAcceptsTasks myTaskSink;
|
||||||
|
|
||||||
|
public BuilderAddColumnWithName(String theColumnName, IAcceptsTasks theTaskSink) {
|
||||||
|
myColumnName = theColumnName;
|
||||||
|
myTaskSink = theTaskSink;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BuilderAddTableRawSql {
|
public BuilderAddColumnWithNameNullable nullable() {
|
||||||
|
return new BuilderAddColumnWithNameNullable(true);
|
||||||
private final AddTableRawSqlTask myTask;
|
|
||||||
|
|
||||||
protected BuilderAddTableRawSql(String theTableName) {
|
|
||||||
myTask = new AddTableRawSqlTask();
|
|
||||||
myTask.setTableName(theTableName);
|
|
||||||
addTask(myTask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BuilderAddColumnWithNameNullable nonNullable() {
|
||||||
public BuilderAddTableRawSql addSql(DriverTypeEnum theDriverTypeEnum, @Language("SQL") String theSql) {
|
return new BuilderAddColumnWithNameNullable(false);
|
||||||
myTask.addSql(theDriverTypeEnum, theSql);
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSql(@Language("SQL") String theSql) {
|
public class BuilderAddColumnWithNameNullable {
|
||||||
myTask.addSql(theSql);
|
private final boolean myNullable;
|
||||||
}
|
|
||||||
|
public BuilderAddColumnWithNameNullable(boolean theNullable) {
|
||||||
|
myNullable = theNullable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BuilderAddTableByColumns extends BuilderWithTableName implements IAcceptsTasks {
|
public void type(AddColumnTask.ColumnTypeEnum theColumnType) {
|
||||||
private final AddTableByColumnTask myTask;
|
type(theColumnType, null);
|
||||||
|
|
||||||
public BuilderAddTableByColumns(IAcceptsTasks theSink, String theTableName, String thePkColumnName) {
|
|
||||||
super(theSink, theTableName);
|
|
||||||
myTask = new AddTableByColumnTask();
|
|
||||||
myTask.setTableName(theTableName);
|
|
||||||
myTask.setPkColumn(thePkColumnName);
|
|
||||||
theSink.addTask(myTask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void type(AddColumnTask.ColumnTypeEnum theColumnType, Integer theLength) {
|
||||||
public BuilderWithTableName.BuilderAddColumnWithName addColumn(String theColumnName) {
|
AddColumnTask task = new AddColumnTask();
|
||||||
return new BuilderWithTableName.BuilderAddColumnWithName(theColumnName, this);
|
task.setColumnName(myColumnName);
|
||||||
|
task.setNullable(myNullable);
|
||||||
|
task.setColumnType(theColumnType);
|
||||||
|
if (theLength != null) {
|
||||||
|
task.setColumnLength(theLength);
|
||||||
|
}
|
||||||
|
myTaskSink.addTask(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addTask(BaseTask<?> theTask) {
|
|
||||||
if (theTask instanceof AddColumnTask) {
|
|
||||||
myTask.addAddColumnTask((AddColumnTask) theTask);
|
|
||||||
} else {
|
|
||||||
super.addTask(theTask);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,28 @@ public class RenameColumnTaskTest extends BaseTest {
|
||||||
assertThat(columnNames.toString(), columnNames, containsInAnyOrder("PID", "TEXTCOL"));
|
assertThat(columnNames.toString(), columnNames, containsInAnyOrder("PID", "TEXTCOL"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBothExistDeleteTargetFirstDataExistsInSourceAndTarget() throws SQLException {
|
||||||
|
executeSql("create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255), myTextCol varchar(255))");
|
||||||
|
executeSql("INSERT INTO SOMETABLE (PID, TEXTCOL, myTextCol) VALUES (123, 'AAA', 'BBB')");
|
||||||
|
|
||||||
|
RenameColumnTask task = new RenameColumnTask();
|
||||||
|
task.setTableName("SOMETABLE");
|
||||||
|
task.setDescription("Drop an index");
|
||||||
|
task.setOldName("myTextCol");
|
||||||
|
task.setNewName("TEXTCOL");
|
||||||
|
task.setDeleteTargetColumnFirstIfBothExist(true);
|
||||||
|
getMigrator().addTask(task);
|
||||||
|
|
||||||
|
try {
|
||||||
|
getMigrator().migrate();
|
||||||
|
fail();
|
||||||
|
} catch (InternalErrorException e) {
|
||||||
|
assertEquals("Failure executing task \"Drop an index\", aborting! Cause: java.sql.SQLException: Can not rename SOMETABLE.myTextCol to TEXTCOL because both columns exist and data exists in TEXTCOL", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testColumnDoesntAlreadyExist() throws SQLException {
|
public void testColumnDoesntAlreadyExist() throws SQLException {
|
||||||
executeSql("create table SOMETABLE (PID bigint not null, myTextCol varchar(255))");
|
executeSql("create table SOMETABLE (PID bigint not null, myTextCol varchar(255))");
|
||||||
|
|
|
@ -222,7 +222,7 @@
|
||||||
<td style="background: #CEC;">4.0.0</td>
|
<td style="background: #CEC;">4.0.0</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>HAPI FHIR 3.8.0-SNAPSHOT</td>
|
<td>HAPI FHIR 3.8.0</td>
|
||||||
<td>JDK8</td>
|
<td>JDK8</td>
|
||||||
<td style="background: #DDD;"></td>
|
<td style="background: #DDD;"></td>
|
||||||
<td style="background: #CEC;">1.0.2</td>
|
<td style="background: #CEC;">1.0.2</td>
|
||||||
|
|
Loading…
Reference in New Issue