From f5788341f2337cb11e01fe28c623737629a9c5e9 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Mon, 23 Sep 2019 18:44:51 -0400 Subject: [PATCH] Add no-shrink-columns option to migrator (#1500) * Add no-shrink-columns option to migrator * Address review comments --- .../fhir/cli/BaseMigrateDatabaseCommand.java | 3 + .../ca/uhn/fhir/jpa/migrate/JdbcUtils.java | 82 +++++++++++++++++-- .../ca/uhn/fhir/jpa/migrate/Migrator.java | 6 ++ .../jpa/migrate/taskdef/ArbitrarySqlTask.java | 2 +- .../taskdef/BaseTableColumnTypeTask.java | 73 +++-------------- .../fhir/jpa/migrate/taskdef/BaseTask.java | 9 ++ .../jpa/migrate/taskdef/ModifyColumnTask.java | 14 +++- .../jpa/migrate/taskdef/AddColumnTest.java | 4 +- .../jpa/migrate/taskdef/ModifyColumnTest.java | 43 ++++++++-- src/changes/changes.xml | 4 + 10 files changed, 156 insertions(+), 84 deletions(-) diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseMigrateDatabaseCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseMigrateDatabaseCommand.java index c4cf570b8f4..72759e380c1 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseMigrateDatabaseCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseMigrateDatabaseCommand.java @@ -78,6 +78,7 @@ public abstract class BaseMigrateDatabaseCommand extends BaseCom addRequiredOption(retVal, "t", "to", "Version", "The database schema version to migrate TO"); 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, "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; } @@ -106,6 +107,7 @@ public abstract class BaseMigrateDatabaseCommand extends BaseCom validateVersionSupported(to); boolean dryRun = theCommandLine.hasOption("r"); + boolean noColumnShrink = theCommandLine.hasOption("no-column-shrink"); String flags = theCommandLine.getOptionValue("x"); myFlags = Arrays.stream(defaultString(flags).split(",")) @@ -119,6 +121,7 @@ public abstract class BaseMigrateDatabaseCommand extends BaseCom migrator.setUsername(username); migrator.setPassword(password); migrator.setDryRun(dryRun); + migrator.setNoColumnShrink(noColumnShrink); addTasks(migrator, from, to); migrator.migrate(); diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java index 412dd261895..f1c889442be 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java @@ -22,6 +22,9 @@ package ca.uhn.fhir.jpa.migrate; import ca.uhn.fhir.jpa.migrate.taskdef.BaseTableColumnTypeTask; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; import org.hibernate.boot.model.naming.Identifier; import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.dialect.internal.StandardDialectResolver; @@ -48,6 +51,73 @@ import static org.thymeleaf.util.StringUtils.toUpperCase; public class JdbcUtils { private static final Logger ourLog = LoggerFactory.getLogger(JdbcUtils.class); + public static class ColumnType { + private final BaseTableColumnTypeTask.ColumnTypeEnum myColumnTypeEnum; + private final Long myLength; + + public ColumnType(BaseTableColumnTypeTask.ColumnTypeEnum theColumnType, Long theLength) { + myColumnTypeEnum = theColumnType; + myLength = theLength; + } + + public ColumnType(BaseTableColumnTypeTask.ColumnTypeEnum theColumnType, int theLength) { + this(theColumnType, (long) theLength); + } + + public ColumnType(BaseTableColumnTypeTask.ColumnTypeEnum theColumnType) { + this(theColumnType, null); + } + + @Override + public boolean equals(Object theO) { + if (this == theO) { + return true; + } + + if (theO == null || getClass() != theO.getClass()) { + return false; + } + + ColumnType that = (ColumnType) theO; + + return new EqualsBuilder() + .append(myColumnTypeEnum, that.myColumnTypeEnum) + .append(myLength, that.myLength) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(myColumnTypeEnum) + .append(myLength) + .toHashCode(); + } + + @Override + public String toString() { + ToStringBuilder b = new ToStringBuilder(this); + b.append("type", myColumnTypeEnum); + if (myLength != null) { + b.append("length", myLength); + } + return b.toString(); + } + + public BaseTableColumnTypeTask.ColumnTypeEnum getColumnTypeEnum() { + return myColumnTypeEnum; + } + + public Long getLength() { + return myLength; + } + + public boolean equals(BaseTableColumnTypeTask.ColumnTypeEnum theColumnType, Long theColumnLength) { + return myColumnTypeEnum == theColumnType && (myLength == null || myLength.equals(theColumnLength)); + } + + } + /** * Retrieve all index names */ @@ -127,7 +197,7 @@ public class JdbcUtils { /** * Retrieve all index names */ - public static String getColumnType(DriverTypeEnum.ConnectionProperties theConnectionProperties, String theTableName, String theColumnName) throws SQLException { + public static ColumnType getColumnType(DriverTypeEnum.ConnectionProperties theConnectionProperties, String theTableName, String theColumnName) throws SQLException { DataSource dataSource = Objects.requireNonNull(theConnectionProperties.getDataSource()); try (Connection connection = dataSource.getConnection()) { return theConnectionProperties.getTxTemplate().execute(t -> { @@ -153,18 +223,18 @@ public class JdbcUtils { Long length = indexes.getLong("COLUMN_SIZE"); switch (dataType) { case Types.VARCHAR: - return BaseTableColumnTypeTask.ColumnTypeEnum.STRING.getDescriptor(length); + return new ColumnType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, length); case Types.NUMERIC: case Types.BIGINT: case Types.DECIMAL: - return BaseTableColumnTypeTask.ColumnTypeEnum.LONG.getDescriptor(null); + return new ColumnType(BaseTableColumnTypeTask.ColumnTypeEnum.LONG, length); case Types.INTEGER: - return BaseTableColumnTypeTask.ColumnTypeEnum.INT.getDescriptor(null); + return new ColumnType(BaseTableColumnTypeTask.ColumnTypeEnum.INT, length); case Types.TIMESTAMP: case Types.TIMESTAMP_WITH_TIMEZONE: - return BaseTableColumnTypeTask.ColumnTypeEnum.DATE_TIMESTAMP.getDescriptor(null); + return new ColumnType(BaseTableColumnTypeTask.ColumnTypeEnum.DATE_TIMESTAMP, length); case Types.BLOB: - return BaseTableColumnTypeTask.ColumnTypeEnum.BLOB.getDescriptor(null); + return new ColumnType(BaseTableColumnTypeTask.ColumnTypeEnum.BLOB, length); default: throw new IllegalArgumentException("Don't know how to handle datatype " + dataType + " for column " + theColumnName + " on table " + theTableName); } diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/Migrator.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/Migrator.java index 1278f1a1dbf..9cadd2feb20 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/Migrator.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/Migrator.java @@ -44,6 +44,7 @@ public class Migrator { private int myChangesCount; private boolean myDryRun; private List myExecutedStatements = new ArrayList<>(); + private boolean myNoColumnShrink; public int getChangesCount() { return myChangesCount; @@ -82,6 +83,7 @@ public class Migrator { next.setDriverType(myDriverType); next.setConnectionProperties(myConnectionProperties); next.setDryRun(myDryRun); + next.setNoColumnShrink(myNoColumnShrink); try { next.execute(); } catch (SQLException e) { @@ -126,4 +128,8 @@ public class Migrator { public void addTasks(List> theTasks) { theTasks.forEach(this::addTask); } + + public void setNoColumnShrink(boolean theNoColumnShrink) { + myNoColumnShrink = theNoColumnShrink; + } } 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 d7acd47efb6..de64bda2543 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 @@ -71,7 +71,7 @@ public class ArbitrarySqlTask extends BaseTask { } for (TableAndColumn next : myConditionalOnExistenceOf) { - String columnType = JdbcUtils.getColumnType(getConnectionProperties(), next.getTable(), next.getColumn()); + JdbcUtils.ColumnType columnType = JdbcUtils.getColumnType(getConnectionProperties(), next.getTable(), next.getColumn()); if (columnType == null) { ourLog.info("Table {} does not have column {} - No action performed", next.getTable(), next.getColumn()); return; diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTypeTask.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTypeTask.java index c5cb793b61d..d16d1f5aaef 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTypeTask.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTypeTask.java @@ -164,74 +164,23 @@ public abstract class BaseTableColumnTypeTask extends B return myColumnLength; } - public BaseTableColumnTypeTask setColumnLength(int theColumnLength) { - myColumnLength = (long) theColumnLength; + public BaseTableColumnTypeTask setColumnLength(long theColumnLength) { + myColumnLength = theColumnLength; return this; } public enum ColumnTypeEnum { - LONG { - @Override - public String getDescriptor(Long theColumnLength) { - Assert.isTrue(theColumnLength == null, "Must not supply a column length"); - return "bigint"; - } - }, - STRING { - @Override - public String getDescriptor(Long theColumnLength) { - Assert.isTrue(theColumnLength != null, "Must supply a column length"); - return "varchar(" + theColumnLength + ")"; - } - }, - DATE_TIMESTAMP { - @Override - public String getDescriptor(Long theColumnLength) { - Assert.isTrue(theColumnLength == null, "Must not supply a column length"); - return "timestamp"; - } - }, - BOOLEAN { - @Override - public String getDescriptor(Long theColumnLength) { - Assert.isTrue(theColumnLength == null, "Must not supply a column length"); - return "boolean"; - } - }, - FLOAT { - @Override - public String getDescriptor(Long theColumnLength) { - Assert.isTrue(theColumnLength == null, "Must not supply a column length"); - return "float"; - } - }, - INT { - @Override - public String getDescriptor(Long theColumnLength) { - Assert.isTrue(theColumnLength == null, "Must not supply a column length"); - return "int"; - } - }, - - BLOB { - @Override - public String getDescriptor(Long theColumnLength) { - Assert.isTrue(theColumnLength == null, "Must not supply a column length"); - return "blob"; - } - }, - - CLOB { - @Override - public String getDescriptor(Long theColumnLength) { - Assert.isTrue(theColumnLength == null, "Must not supply a column length"); - return "clob"; - } - }; - - public abstract String getDescriptor(Long theColumnLength); + LONG, + STRING, + DATE_TIMESTAMP, + BOOLEAN, + FLOAT, + INT, + BLOB, + CLOB + ; } 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 b4c03979b2b..d8986ce325b 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 @@ -42,6 +42,15 @@ public abstract class BaseTask { private int myChangesCount; private boolean myDryRun; private List myExecutedStatements = new ArrayList<>(); + private boolean myNoColumnShrink; + + public boolean isNoColumnShrink() { + return myNoColumnShrink; + } + + public void setNoColumnShrink(boolean theNoColumnShrink) { + myNoColumnShrink = theNoColumnShrink; + } public boolean isDryRun() { return myDryRun; 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 6442a4a27c9..7e222905ce1 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 @@ -36,7 +36,7 @@ public class ModifyColumnTask extends BaseTableColumnTypeTask @Override public void execute() throws SQLException { - String existingType; + JdbcUtils.ColumnType existingType; boolean nullable; Set columnNames = JdbcUtils.getColumnNames(getConnectionProperties(), getTableName()); @@ -52,11 +52,17 @@ public class ModifyColumnTask extends BaseTableColumnTypeTask throw new InternalErrorException(e); } - String wantedType = getColumnType().getDescriptor(getColumnLength()); - boolean alreadyOfCorrectType = existingType.equals(wantedType); + if (isNoColumnShrink()) { + long existingLength = existingType.getLength() != null ? existingType.getLength() : 0; + if (existingLength > getColumnLength()) { + setColumnLength(existingLength); + } + } + + boolean alreadyOfCorrectType = existingType.equals(getColumnType(), getColumnLength()); boolean alreadyCorrectNullable = isNullable() == nullable; if (alreadyOfCorrectType && alreadyCorrectNullable) { - ourLog.info("Column {} on table {} is already of type {} and has nullable {} - No action performed", getColumnName(), getTableName(), wantedType, nullable); + ourLog.info("Column {} on table {} is already of type {} and has nullable {} - No action performed", getColumnName(), getTableName(), existingType, nullable); return; } diff --git a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTest.java b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTest.java index cc0ba181df7..c809412e6f0 100644 --- a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTest.java +++ b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTest.java @@ -40,8 +40,8 @@ public class AddColumnTest extends BaseTest { getMigrator().migrate(); - String type = JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "newcolint"); - assertEquals(BaseTableColumnTypeTask.ColumnTypeEnum.INT.getDescriptor(null), type); + JdbcUtils.ColumnType type = JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "newcolint"); + assertEquals(BaseTableColumnTypeTask.ColumnTypeEnum.INT, type.getColumnTypeEnum()); } @Test diff --git a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTest.java b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTest.java index cf17003797f..6a2cb7c63d3 100644 --- a/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTest.java +++ b/hapi-fhir-jpaserver-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTest.java @@ -25,7 +25,32 @@ public class ModifyColumnTest extends BaseTest { getMigrator().migrate(); - assertEquals("varchar(300)", JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "TEXTCOL")); + assertEquals(new JdbcUtils.ColumnType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 300), JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "TEXTCOL")); + assertEquals(1, task.getExecutedStatements().size()); + + // Make sure additional migrations don't crash + getMigrator().migrate(); + getMigrator().migrate(); + + } + + @Test + public void testNoShrink_SameNullable() throws SQLException { + executeSql("create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255), newcol bigint)"); + + ModifyColumnTask task = new ModifyColumnTask(); + task.setTableName("SOMETABLE"); + task.setColumnName("TEXTCOL"); + task.setColumnType(AddColumnTask.ColumnTypeEnum.STRING); + task.setNullable(true); + task.setColumnLength(200); + + getMigrator().setNoColumnShrink(true); + getMigrator().addTask(task); + getMigrator().migrate(); + + assertEquals(0, task.getExecutedStatements().size()); + assertEquals(new JdbcUtils.ColumnType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 255), JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "TEXTCOL")); // Make sure additional migrations don't crash getMigrator().migrate(); @@ -38,8 +63,8 @@ public class ModifyColumnTest extends BaseTest { executeSql("create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255) not null)"); assertFalse(JdbcUtils.isColumnNullable(getConnectionProperties(), "SOMETABLE", "PID")); assertFalse(JdbcUtils.isColumnNullable(getConnectionProperties(), "SOMETABLE", "TEXTCOL")); - assertEquals("bigint", JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "PID")); - assertEquals("varchar(255)", JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "TEXTCOL")); + assertEquals(new JdbcUtils.ColumnType(BaseTableColumnTypeTask.ColumnTypeEnum.LONG, 19), JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "PID")); + assertEquals(new JdbcUtils.ColumnType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 255), JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "TEXTCOL")); // PID ModifyColumnTask task = new ModifyColumnTask(); @@ -63,8 +88,8 @@ public class ModifyColumnTest extends BaseTest { assertTrue(JdbcUtils.isColumnNullable(getConnectionProperties(), "SOMETABLE", "PID")); assertTrue(JdbcUtils.isColumnNullable(getConnectionProperties(), "SOMETABLE", "TEXTCOL")); - assertEquals("bigint", JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "PID")); - assertEquals("varchar(255)", JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "TEXTCOL")); + assertEquals(new JdbcUtils.ColumnType(BaseTableColumnTypeTask.ColumnTypeEnum.LONG, 19), JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "PID")); + assertEquals(new JdbcUtils.ColumnType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 255), JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "TEXTCOL")); // Make sure additional migrations don't crash getMigrator().migrate(); @@ -78,8 +103,8 @@ public class ModifyColumnTest extends BaseTest { executeSql("create table SOMETABLE (PID bigint, TEXTCOL varchar(255))"); assertTrue(JdbcUtils.isColumnNullable(getConnectionProperties(), "SOMETABLE", "PID")); assertTrue(JdbcUtils.isColumnNullable(getConnectionProperties(), "SOMETABLE", "TEXTCOL")); - assertEquals("bigint", JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "PID")); - assertEquals("varchar(255)", JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "TEXTCOL")); + assertEquals(new JdbcUtils.ColumnType(BaseTableColumnTypeTask.ColumnTypeEnum.LONG, 19), JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "PID")); + assertEquals(new JdbcUtils.ColumnType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 255), JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "TEXTCOL")); // PID ModifyColumnTask task = new ModifyColumnTask(); @@ -103,8 +128,8 @@ public class ModifyColumnTest extends BaseTest { assertFalse(JdbcUtils.isColumnNullable(getConnectionProperties(), "SOMETABLE", "PID")); assertFalse(JdbcUtils.isColumnNullable(getConnectionProperties(), "SOMETABLE", "TEXTCOL")); - assertEquals("bigint", JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "PID")); - assertEquals("varchar(255)", JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "TEXTCOL")); + assertEquals(new JdbcUtils.ColumnType(BaseTableColumnTypeTask.ColumnTypeEnum.LONG, 19), JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "PID")); + assertEquals(new JdbcUtils.ColumnType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 255), JdbcUtils.getColumnType(getConnectionProperties(), "SOMETABLE", "TEXTCOL")); // Make sure additional migrations don't crash getMigrator().migrate(); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index f754c34e94c..b9f335b5949 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -193,6 +193,10 @@ resulted in some ValueSets with duplicate codes. This has been corrected by specifying a path with each filename. + + A new flag has been added to the JPA migrator tool that causes the migrator to not try to reduce the length + of existing columns in the schema. + Some resource IDs and URLs for LOINC ValueSets and ConceptMaps were inconsistently populated by the terminology uploader. This has been corrected.