diff --git a/src/main/java/com/avaje/ebean/config/DbMigrationConfig.java b/src/main/java/com/avaje/ebean/config/DbMigrationConfig.java index 94bc86b01..64dc48c2f 100644 --- a/src/main/java/com/avaje/ebean/config/DbMigrationConfig.java +++ b/src/main/java/com/avaje/ebean/config/DbMigrationConfig.java @@ -61,11 +61,6 @@ public class DbMigrationConfig { */ protected String modelPath = "model"; - /** - * Subdirectory the drop ddl scripts go into. - */ - protected String dropPath = "drop"; - /** * Subdirectory the rollback ddl scripts go into. */ @@ -76,11 +71,6 @@ public class DbMigrationConfig { */ protected String applySuffix = ".sql"; - /** - * Default drop script suffix to ddl so that it isn't picked up by FlywayDb. - */ - protected String dropSuffix = ".drop.ddl"; - /** * Default rollback script suffix to ddl so that it isn't picked up by FlywayDb. */ @@ -90,6 +80,11 @@ public class DbMigrationConfig { protected boolean includeGeneratedFileComment; + /** + * The version of a pending drop that should be generated as the next migration. + */ + protected String generatePendingDrop; + /** * Return the DB platform to generate migration DDL for. * @@ -140,20 +135,6 @@ public class DbMigrationConfig { this.modelPath = modelPath; } - /** - * Return the relative path for the drop ddl scripts (defaults to drop). - */ - public String getDropPath() { - return dropPath; - } - - /** - * Set the relative path for the drop ddl scripts (defaults to drop). - */ - public void setDropPath(String dropPath) { - this.dropPath = dropPath; - } - /** * Return the relative path for the rollback ddl scripts (defaults to rollback). */ @@ -210,20 +191,6 @@ public class DbMigrationConfig { this.applySuffix = applySuffix; } - /** - * Return the drop script suffix (defaults to ddl so that it isn't picked up by FlywayDb). - */ - public String getDropSuffix() { - return dropSuffix; - } - - /** - * Set the drop script suffix (defaults to ddl so that it isn't picked up by FlywayDb). - */ - public void setDropSuffix(String dropSuffix) { - this.dropSuffix = dropSuffix; - } - /** * Return the rollback script suffix (defaults to ddl so that it isn't picked up by FlywayDb). */ @@ -252,6 +219,20 @@ public class DbMigrationConfig { this.includeGeneratedFileComment = includeGeneratedFileComment; } + /** + * Return the migration version (or "next") to generate pending drops for. + */ + public String getGeneratePendingDrop() { + return generatePendingDrop; + } + + /** + * Set the migration version (or "next") to generate pending drops for. + */ + public void setGeneratePendingDrop(String generatePendingDrop) { + this.generatePendingDrop = generatePendingDrop; + } + /** * Set the migration version. *

@@ -275,7 +256,6 @@ public class DbMigrationConfig { * into a single directory. */ public void singleDirectory() { - this.dropPath = ""; this.rollbackPath = ""; this.modelPath = ""; } @@ -291,13 +271,12 @@ public class DbMigrationConfig { } else { modelPath = properties.get("migration.modelPath", modelPath); rollbackPath = properties.get("migration.rollbackPath", rollbackPath); - dropPath = properties.get("migration.dropPath", dropPath); } applySuffix = properties.get("migration.applySuffix", applySuffix); - dropSuffix = properties.get("migration.dropSuffix", dropSuffix); rollbackSuffix = properties.get("migration.rollbackSuffix", rollbackSuffix); modelSuffix = properties.get("migration.modelSuffix", modelSuffix); includeGeneratedFileComment = properties.getBoolean("migration.includeGeneratedFileComment", includeGeneratedFileComment); + generatePendingDrop = properties.get("migration.generatePendingDrop", generatePendingDrop); platform = properties.getEnum(DbPlatformName.class, "migration.platform", platform); suppressRollback = properties.getBoolean("migration.suppressRollback", suppressRollback); @@ -324,7 +303,6 @@ public class DbMigrationConfig { return generate; } - /** * Called by EbeanServer on start. * diff --git a/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java b/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java index 65d71b60a..f4cbc9701 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java +++ b/src/main/java/com/avaje/ebean/dbmigration/DbMigration.java @@ -20,6 +20,7 @@ import com.avaje.ebean.dbmigration.migrationreader.MigrationXmlWriter; import com.avaje.ebean.dbmigration.model.CurrentModel; import com.avaje.ebean.dbmigration.model.MConfiguration; import com.avaje.ebean.dbmigration.model.MigrationModel; +import com.avaje.ebean.dbmigration.model.MigrationVersion; import com.avaje.ebean.dbmigration.model.ModelContainer; import com.avaje.ebean.dbmigration.model.ModelDiff; import com.avaje.ebean.dbmigration.model.PlatformDdlWriter; @@ -201,46 +202,15 @@ public class DbMigration { if (!online) { DbOffline.setRunningMigration(); } - setDefaults(); - try { + Request request = createRequest(); - File migrationDir = getMigrationDirectory(); - File modelDir = getModelDirectory(migrationDir); - - MigrationModel migrationModel = new MigrationModel(modelDir, migrationConfig.getModelSuffix()); - ModelContainer migrated = migrationModel.read(); - - CurrentModel currentModel = new CurrentModel(server, constraintNaming); - ModelContainer current = currentModel.read(); - - ModelDiff diff = new ModelDiff(migrated); - diff.compareTo(current); - - if (diff.isEmpty()) { - logger.info("no changes detected - no migration written"); - return; - } - - // there were actually changes to write - Migration dbMigration = diff.getMigration(); - - String fullVersion = getFullVersion(migrationModel); - - logger.info("generating migration:{}", fullVersion); - if (!writeMigrationXml(dbMigration, modelDir, fullVersion)) { - logger.warn("migration already exists, not generating DDL"); - + String pendingVersion = generatePendingDrop(); + if (pendingVersion != null) { + generatePendingDrop(request, pendingVersion); } else { - if (databasePlatform != null) { - // writer needs the current model to provide table/column details for - // history ddl generation (triggers, history tables etc) - DdlWrite write = new DdlWrite(new MConfiguration(), current); - PlatformDdlWriter writer = createDdlWriter(databasePlatform, ""); - writer.processMigration(dbMigration, write, migrationDir , fullVersion); - } - writeExtraPlatformDdl(fullVersion, currentModel, dbMigration, migrationDir); + generateDiff(request); } } finally { @@ -250,10 +220,127 @@ public class DbMigration { } } + private void generateDiff(Request request) throws IOException { + + if (request.hasPendingDrops()) { + logger.info("Pending un-applied drops in versions {}", request.getPendingDrops()); + } + + ModelDiff diff = request.createDiff(); + if (diff.isEmpty()) { + logger.info("no changes detected - no migration written"); + } else { + // there were actually changes to write + generateMigration(request, diff.getMigration(), null); + } + } + + private void generatePendingDrop(Request request, String pendingVersion) throws IOException { + + Migration migration = request.migrationForPendingDrop(pendingVersion); + + generateMigration(request, migration, pendingVersion); + if (request.hasPendingDrops()) { + logger.info("... remaining pending un-applied drops in versions {}", request.getPendingDrops()); + } + } + + private Request createRequest() { + return new Request(); + } + + private class Request { + + final File migrationDir; + final File modelDir; + final MigrationModel migrationModel; + final CurrentModel currentModel; + final ModelContainer migrated; + final ModelContainer current; + + private Request() { + this.migrationDir = getMigrationDirectory(); + this.modelDir = getModelDirectory(migrationDir); + this.migrationModel = new MigrationModel(modelDir, migrationConfig.getModelSuffix()); + this.migrated = migrationModel.read(); + this.currentModel = new CurrentModel(server, constraintNaming); + this.current = currentModel.read(); + } + + /** + * Return true if there are pending un-applied drops. + */ + public boolean hasPendingDrops() { + return migrated.hasPendingDrops(); + } + + /** + * Return the migration for the pending drops for a given version. + */ + public Migration migrationForPendingDrop(String pendingVersion) { + + Migration migration = migrated.migrationForPendingDrop(pendingVersion); + + // register any remaining pending drops + migrated.registerPendingHistoryDropColumns(current); + return migration; + } + + /** + * Return the list of versions that have pending un-applied drops. + */ + public List getPendingDrops() { + return migrated.getPendingDrops(); + } + + /** + * Create an return the diff of the current model to the migration model. + */ + public ModelDiff createDiff() { + ModelDiff diff = new ModelDiff(migrated); + diff.compareTo(current); + return diff; + } + } + + private void generateMigration(Request request, Migration dbMigration, String dropsFor) throws IOException { + + String fullVersion = getFullVersion(request.migrationModel, dropsFor); + + logger.info("generating migration:{}", fullVersion); + if (!writeMigrationXml(dbMigration, request.modelDir, fullVersion)) { + logger.warn("migration already exists, not generating DDL"); + + } else { + if (databasePlatform != null) { + // writer needs the current model to provide table/column details for + // history ddl generation (triggers, history tables etc) + DdlWrite write = new DdlWrite(new MConfiguration(), request.current); + PlatformDdlWriter writer = createDdlWriter(databasePlatform, ""); + writer.processMigration(dbMigration, write, request.migrationDir , fullVersion); + } + writeExtraPlatformDdl(fullVersion, request.currentModel, dbMigration, request.migrationDir); + } + } + + /** + * Return true if the next pending drop changeSet should be generated as the next migration. + */ + private String generatePendingDrop() { + + String nextDrop = System.getProperty("ddl.migration.pendingDrop"); + if (nextDrop != null) { + return nextDrop; + } + return migrationConfig.getGeneratePendingDrop(); + } + /** * Return the full version for the migration being generated. + * + * The full version can contain a comment suffix after a "__" double underscore. */ - private String getFullVersion(MigrationModel migrationModel) { + private String getFullVersion(MigrationModel migrationModel, String dropsFor) { String version = migrationConfig.getVersion(); if (version == null) { @@ -261,10 +348,14 @@ public class DbMigration { } String fullVersion = version; + if (migrationConfig.getName() != null) { + fullVersion += "__" + toUnderScore(migrationConfig.getName()); - String name = migrationConfig.getName(); - if (name != null) { - fullVersion += "__" + toUnderScore(name); + } else if (dropsFor != null) { + fullVersion += "__" + toUnderScore("dropsFor_" + MigrationVersion.trim(dropsFor)); + + } else if (version.equals(initialVersion)) { + fullVersion += "__initial"; } return fullVersion; } diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlWrite.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlWrite.java index 4c60c18f2..f2f0fe279 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlWrite.java +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/DdlWrite.java @@ -12,8 +12,7 @@ public class DdlWrite { public enum Mode { APPLY, - ROLLBACK, - DROP + ROLLBACK } private final ModelContainer currentModel; @@ -32,28 +31,6 @@ public class DdlWrite { private final DdlBuffer rollback; - /** - * For DDL that drops tables and columns etc. - * - * This DDL typically can not run automatically in production as there is most commonly - * existing servers running the application using these tables and columns. Typically - * these drop statements may be executed AFTER all the servers in the application have - * migrated onto new code. - */ - private final DdlBuffer drop; - - /** - * For use when History is turned off for a base table or history is no longer - * desired on specific columns. This DDL should typically execute manually after review - * by DBA's. - */ - private final DdlBuffer dropHistory; - - /** - * Buffer used to drop dependencies early in the 'drop script'. - */ - private final DdlBuffer dropDropDependencies; - /** * Create without any configuration or current model (no history support). */ @@ -73,9 +50,6 @@ public class DdlWrite { this.rollbackDropDependencies = new BaseDdlBuffer(configuration); this.rollbackForeignKeys = new BaseDdlBuffer(configuration); this.rollback = new BaseDdlBuffer(configuration); - this.drop = new BaseDdlBuffer(configuration); - this.dropHistory = new BaseDdlBuffer(configuration); - this.dropDropDependencies = new BaseDdlBuffer(configuration); } /** @@ -115,7 +89,6 @@ public class DdlWrite { switch (mode) { case APPLY: return apply(); case ROLLBACK: return rollback(); - case DROP: return drop(); default: throw new IllegalStateException("Invalid mode" + mode); } @@ -128,7 +101,6 @@ public class DdlWrite { switch (mode) { case APPLY: return applyHistory(); case ROLLBACK: return rollback(); - case DROP: return dropHistory(); default: throw new IllegalStateException("Invalid mode" + mode); } @@ -142,20 +114,11 @@ public class DdlWrite { switch (mode) { case APPLY: return applyDropDependencies(); case ROLLBACK: return rollbackDropDependencies(); - case DROP: return dropDropDependencies(); default: throw new IllegalStateException("Invalid mode" + mode); } } - - /** - * Return true the drop buffers are empty. - */ - public boolean isDropEmpty() { - return drop.getBuffer().isEmpty() && dropHistory.getBuffer().isEmpty(); - } - /** * Return the buffer that APPLY DDL is written to. */ @@ -216,26 +179,8 @@ public class DdlWrite { return rollback; } - /** - * Return the buffer that destructive changes are written to. This is typically drop table and - * drop column. - */ public DdlBuffer drop() { - return drop; - } - - /** - * Return the buffer that is used when history is no longer required on a table or specific columns. - */ - public DdlBuffer dropHistory() { - return dropHistory; - } - - /** - * Return the buffer that executes early for 'drop' script. - */ - public DdlBuffer dropDropDependencies() { - return dropDropDependencies; + return apply; } } diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java index b70b98d7d..f6925cc55 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/DbTriggerBasedHistoryDdl.java @@ -71,37 +71,23 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl { DbTriggerUpdate triggerUpdate = createDbTriggerUpdate(writer, table); - if (update.hasApplyChanges()) { - // includes add, include and exclude column changes + String description = update.description(); + List includedColumns = columnNamesForApply(table); - String applyChangeDescription = update.descriptionForApply(); - List includedColumns = columnNamesForApply(table); + DdlBuffer apply = writer.applyHistory(); + apply.append("-- changes: ").append(description).newLine(); - DdlBuffer apply = writer.applyHistory(); - apply.append("-- changes: ").append(applyChangeDescription).newLine(); + triggerUpdate.prepare(DdlWrite.Mode.APPLY, includedColumns); + updateHistoryTriggers(triggerUpdate); - triggerUpdate.prepare(DdlWrite.Mode.APPLY, includedColumns); - updateHistoryTriggers(triggerUpdate); + // put a reverted version into the rollback buffer + update.toRevertedColumns(includedColumns); - // put a reverted version into the rollback buffer - update.toRevertedColumns(includedColumns); + DdlBuffer rollback = writer.rollback(); + rollback.append("-- revert changes: ").append(description).newLine(); - DdlBuffer rollback = writer.rollback(); - rollback.append("-- revert changes: ").append(applyChangeDescription).newLine(); - - triggerUpdate.prepare(DdlWrite.Mode.ROLLBACK, includedColumns); - updateHistoryTriggers(triggerUpdate);//writer, DdlWrite.Mode.ROLLBACK, baseTableName, historyTableName, includedColumns); - } - - if (update.hasDropChanges()) { - // effectively applies the dropped columns changes to history triggers - - DdlBuffer drop = writer.dropHistory(); - drop.append("-- changes: ").append(update.descriptionForDrop()).newLine(); - - triggerUpdate.prepare(DdlWrite.Mode.DROP, columnNamesForDrop(table)); - updateHistoryTriggers(triggerUpdate);//writer, DdlWrite.Mode.DROP, baseTableName, historyTableName, columnNamesForDrop(table)); - } + triggerUpdate.prepare(DdlWrite.Mode.ROLLBACK, includedColumns); + updateHistoryTriggers(triggerUpdate); } protected DbTriggerUpdate createDbTriggerUpdate(DdlWrite writer, MTable table) { @@ -116,11 +102,10 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl { String baseTable = dropHistoryTable.getBaseTable(); // drop in appropriate order - dropTriggers(writer.dropHistory(), baseTable); - dropHistoryTableEtc(writer.dropHistory(), baseTable); + dropTriggers(writer.applyDropDependencies(), baseTable); + dropHistoryTableEtc(writer.applyDropDependencies(), baseTable); } - @Override public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException { @@ -315,11 +300,4 @@ public abstract class DbTriggerBasedHistoryDdl implements PlatformHistoryDdl { return table.allHistoryColumns(true); } - /** - * Return the column names included in history for the drop script. - */ - protected List columnNamesForDrop(MTable table) throws IOException { - - return table.allHistoryColumns(false); - } } diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/HistoryTableUpdate.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/HistoryTableUpdate.java index ce83ee0bb..a7f929b9c 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/HistoryTableUpdate.java +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/HistoryTableUpdate.java @@ -10,7 +10,6 @@ import java.util.List; */ public class HistoryTableUpdate { - /** * Column change type. */ @@ -32,12 +31,12 @@ public class HistoryTableUpdate { this.column = column; } - public String description() { - return change.name().toLowerCase()+" "+column; + public String toString() { + return description(); } - private boolean isChangeFor(boolean apply) { - return apply ? change != Change.DROP : change == Change.DROP; + public String description() { + return change.name().toLowerCase()+" "+column; } private void revert(List includedColumns) { @@ -47,12 +46,11 @@ public class HistoryTableUpdate { includedColumns.remove(column); break; } - case EXCLUDE: { + case EXCLUDE: + case DROP: { includedColumns.add(column); break; } - case DROP: - break; default: throw new IllegalStateException("Unexpected change "+change); } @@ -70,60 +68,12 @@ public class HistoryTableUpdate { this.baseTable = baseTable; } - private boolean isChangeFor(boolean apply) { - for (Column columnChange : columnChanges) { - if (columnChange.isChangeFor(apply)) { - return true; - } - } - return false; - } - - /** - * Return true if the change includes apply changes (ADD, INCLUDE, EXCLUDE). - */ - public boolean hasApplyChanges() { - return isChangeFor(true); - } - - /** - * Return true if the change includes DROP column. - */ - public boolean hasDropChanges() { - return isChangeFor(false); - } - /** * Return a description of the changes that cause the history trigger/function - * to be regenerated (added, included or excluded columns). + * to be regenerated (added, included, excluded and dropped columns). */ - public String descriptionForApply() { - return descriptionFor(true); - } - - /** - * Return a description of the changes that cause the history trigger/function - * to be regenerated in the drop script (dropped columns only). - */ - public String descriptionForDrop() { - return descriptionFor(false); - } - - private String descriptionFor(boolean apply) { - - StringBuilder sb = new StringBuilder(90); - boolean first = true; - for (Column column : columnChanges) { - if (column.isChangeFor(apply)) { - if (first) { - first = false; - } else { - sb.append(", "); - } - sb.append(column.description()); - } - } - return sb.toString(); + public String description() { + return columnChanges.toString(); } /** diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java b/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java index 9cefa6407..a835aeb71 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSet.java @@ -27,6 +27,7 @@ import javax.xml.bind.annotation.XmlType; * </choice> * </sequence> * <attribute name="type" use="required" type="{http://ebean-orm.github.io/xml/ns/dbmigration}changeSetType" /> + * <attribute name="dropsFor" type="{http://www.w3.org/2001/XMLSchema}string" /> * <attribute name="generated" type="{http://www.w3.org/2001/XMLSchema}boolean" /> * <attribute name="author" type="{http://www.w3.org/2001/XMLSchema}string" /> * <attribute name="comment" type="{http://www.w3.org/2001/XMLSchema}string" /> @@ -62,6 +63,8 @@ public class ChangeSet { protected List changeSetChildren; @XmlAttribute(name = "type", required = true) protected ChangeSetType type; + @XmlAttribute(name = "dropsFor") + protected String dropsFor; @XmlAttribute(name = "generated") protected Boolean generated; @XmlAttribute(name = "author") @@ -134,6 +137,30 @@ public class ChangeSet { this.type = value; } + /** + * Gets the value of the dropsFor property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getDropsFor() { + return dropsFor; + } + + /** + * Sets the value of the dropsFor property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setDropsFor(String value) { + this.dropsFor = value; + } + /** * Gets the value of the generated property. * diff --git a/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSetType.java b/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSetType.java index c60bd97ab..b87dffebb 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSetType.java +++ b/src/main/java/com/avaje/ebean/dbmigration/migration/ChangeSetType.java @@ -15,7 +15,7 @@ import javax.xml.bind.annotation.XmlType; * <simpleType name="changeSetType"> * <restriction base="{http://www.w3.org/2001/XMLSchema}string"> * <enumeration value="apply"/> - * <enumeration value="drop"/> + * <enumeration value="pendingDrops"/> * <enumeration value="baseline"/> * </restriction> * </simpleType> @@ -28,8 +28,8 @@ public enum ChangeSetType { @XmlEnumValue("apply") APPLY("apply"), - @XmlEnumValue("drop") - DROP("drop"), + @XmlEnumValue("pendingDrops") + PENDING_DROPS("pendingDrops"), @XmlEnumValue("baseline") BASELINE("baseline"); private final String value; diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java index cf246dc0c..cc9d97d0f 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java @@ -132,14 +132,14 @@ public class MTable { */ public MTable createDraftTable() { - draftTable = new MTable(name+"_draft"); + draftTable = new MTable(name + "_draft"); draftTable.draft = true; draftTable.whenCreatedColumn = whenCreatedColumn; // compoundKeys // compoundUniqueConstraints draftTable.identityType = identityType; - for (MColumn col: allColumns()) { + for (MColumn col : allColumns()) { draftTable.addColumn(col.copyForDraft()); } @@ -546,20 +546,19 @@ public class MTable { // These dropColumns should occur on the history // table as well as the base table dropColumn.setWithHistory(Boolean.TRUE); - newTable.registerDroppedColumn(existingColumn.getName(), columnPosition); } modelDiff.addDropColumn(dropColumn); } /** - * Register a dropped column with it's previous column position. - * We need this for history triggers and views as we don't actually drop the - * column until the 'drop script' is run so the 'apply script' for history changes - * still needs to include the columns that are going to be dropped. + * Register a pending un-applied drop column. + *

+ * This means this column still needs to be included in history views/triggers etc even + * though it is not part of the current model. */ - protected void registerDroppedColumn(String name, int columnPosition) { - droppedColumns.add(new DroppedColumn(name, columnPosition)); + public void registerPendingDropColumn(String columnName) { + droppedColumns.add(new DroppedColumn(columnName, 100)); } private static class DroppedColumn implements Comparable { @@ -573,7 +572,7 @@ public class MTable { @Override public int compareTo(DroppedColumn o) { - return Integer.compare(o.columnPosition, columnPosition); + return (o.columnPosition < columnPosition) ? -1 : ((o.columnPosition == columnPosition) ? 0 : 1); } } @@ -588,7 +587,7 @@ public class MTable { /** * Check if there are duplicate foreign keys. *

- * This can occur when an ManyToMany relates back to itself. + * This can occur when an ManyToMany relates back to itself. *

*/ public void checkDuplicateForeignKeys() { @@ -639,7 +638,7 @@ public class MTable { */ private String extractBaseTable(String references) { int lastDot = references.lastIndexOf('.'); - return references.substring(0,lastDot); + return references.substring(0, lastDot); } /** @@ -648,7 +647,7 @@ public class MTable { */ private String deriveReferences(String references, String draftTableName) { int lastDot = references.lastIndexOf('.'); - return draftTableName+"."+references.substring(lastDot+1); + return draftTableName + "." + references.substring(lastDot + 1); } } diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MigrationModel.java b/src/main/java/com/avaje/ebean/dbmigration/model/MigrationModel.java index 79b73fb73..b621bb6ce 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/MigrationModel.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MigrationModel.java @@ -52,7 +52,7 @@ public class MigrationModel { List resources = new ArrayList(); for (File xmlFile: xmlFiles) { - resources.add(new MigrationResource(xmlFile)); + resources.add(new MigrationResource(xmlFile, createVersion(xmlFile))); } // sort into version order before applying @@ -60,7 +60,7 @@ public class MigrationModel { for (MigrationResource migrationResource: resources) { logger.debug("read {}", migrationResource); - model.apply(migrationResource.read()); + model.apply(migrationResource.read(), migrationResource.getVersion()); } // remember the last version @@ -69,6 +69,12 @@ public class MigrationModel { } } + private MigrationVersion createVersion(File xmlFile) { + String fileName = xmlFile.getName(); + String versionName = fileName.substring(0, fileName.length() - modelSuffix.length()); + return MigrationVersion.parse(versionName); + } + public String getNextVersion(String initialVersion) { return lastVersion == null ? initialVersion : lastVersion.nextVersion(); diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MigrationResource.java b/src/main/java/com/avaje/ebean/dbmigration/model/MigrationResource.java index 15f62aaf8..19b47f62d 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/MigrationResource.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MigrationResource.java @@ -17,9 +17,9 @@ public class MigrationResource implements Comparable { /** * Construct with a migration xml file. */ - public MigrationResource(File migrationFile) { + public MigrationResource(File migrationFile, MigrationVersion version) { this.migrationFile = migrationFile; - this.version = MigrationVersion.parse(migrationFile.getName()); + this.version = version; } public String toString() { diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MigrationVersion.java b/src/main/java/com/avaje/ebean/dbmigration/model/MigrationVersion.java index 8bb5f5ed9..f42e8755c 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/MigrationVersion.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MigrationVersion.java @@ -1,5 +1,7 @@ package com.avaje.ebean.dbmigration.model; +import java.util.Arrays; + /** * The version of a migration used so that migrations are processed in order. */ @@ -15,23 +17,74 @@ public class MigrationVersion implements Comparable { */ private final int[] ordering; - private MigrationVersion(String raw, int[] ordering) { + private final boolean[] underscores; + + private final String comment; + + private MigrationVersion(String raw, int[] ordering, boolean[] underscores, String comment) { this.raw = raw; this.ordering = ordering; + this.underscores = underscores; + this.comment = comment; } public String toString() { return raw; } + /** + * Return the version comment. + */ + public String getComment() { + return comment; + } + /** + * Return the version in raw form. + */ + public String getRaw() { + return raw; + } + + /** + * Return the trimmed version excluding version comment and un-parsable string. + */ + public String asString() { + return formattedVersion(false, false); + } + + /** + * Return the trimmed version with any underscores replaced with '.' + */ + public String normalised() { + return formattedVersion(true, false); + } + + /** + * Return the next version based on this version. + */ public String nextVersion() { + return formattedVersion(false, true); + } + + /** + * Returns the version part of the string. + * + * Normalised means always use '.' delimiters (no underscores). + * NextVersion means bump/increase the last version number by 1. + */ + private String formattedVersion(boolean normalised, boolean nextVersion) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < ordering.length; i++) { - if (i < ordering.length -1 ) { - sb.append(ordering[i]).append("."); + if (i < ordering.length - 1) { + sb.append(ordering[i]); + if (normalised) { + sb.append('.'); + } else { + sb.append(underscores[i] ? '_' : '.'); + } } else { - sb.append(ordering[i]+1); + sb.append((nextVersion) ? ordering[i] + 1 : ordering[i]); } } return sb.toString(); @@ -54,33 +107,54 @@ public class MigrationVersion implements Comparable { return 0; } + /** + * Parse the raw version string and just return the leading version number; + */ + public static String trim(String raw) { + return parse(raw).asString(); + } + /** * Parse the raw version string into a MigrationVersion. */ public static MigrationVersion parse(String raw) { - String value = raw.replace("__","."); - value = value.replace('_','.'); + String comment = ""; + String value = raw; + int commentStart = raw.indexOf("__"); + if (commentStart > -1) { + // trim off the trailing comment + comment = raw.substring(commentStart + 2); + value = value.substring(0, commentStart); + } + + value = value.replace('_', '.'); String[] sections = value.split("\\."); + boolean[] underscores = new boolean[sections.length]; int[] ordering = new int[sections.length]; + int delimiterPos = 0; int stopIndex = 0; for (int i = 0; i < sections.length; i++) { try { ordering[i] = Integer.parseInt(sections[i]); stopIndex++; + + delimiterPos += sections[i].length(); + underscores[i] = (delimiterPos < raw.length() - 1 && raw.charAt(delimiterPos) == '_'); + delimiterPos++; } catch (NumberFormatException e) { // stop parsing break; } } - int[] actualOrder = new int[stopIndex]; - System.arraycopy(ordering, 0, actualOrder, 0, stopIndex); + int[] actualOrder = Arrays.copyOf(ordering, stopIndex); + boolean[] actualUnderscores = Arrays.copyOf(underscores, stopIndex); - return new MigrationVersion(raw, actualOrder); + return new MigrationVersion(raw, actualOrder, actualUnderscores, comment); } } diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java index 071ab5fa1..2ec4839e1 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java @@ -4,6 +4,7 @@ import com.avaje.ebean.dbmigration.migration.AddColumn; import com.avaje.ebean.dbmigration.migration.AddHistoryTable; import com.avaje.ebean.dbmigration.migration.AlterColumn; import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.ChangeSetType; import com.avaje.ebean.dbmigration.migration.CreateIndex; import com.avaje.ebean.dbmigration.migration.CreateTable; import com.avaje.ebean.dbmigration.migration.DropColumn; @@ -28,15 +29,16 @@ public class ModelContainer { /** * All the tables in the model. */ - private Map tables = new LinkedHashMap(); + private final Map tables = new LinkedHashMap(); /** * All the non unique non foreign key indexes. */ - private Map indexes = new LinkedHashMap(); + private final Map indexes = new LinkedHashMap(); + + private final PendingDrops pendingDrops = new PendingDrops(); public ModelContainer() { - } /** @@ -82,14 +84,31 @@ public class ModelContainer { /** * Apply a migration with associated changeSets to the model. */ - public void apply(Migration migration) { + public void apply(Migration migration, MigrationVersion version) { List changeSets = migration.getChangeSet(); for (ChangeSet changeSet : changeSets) { - applyChangeSet(changeSet); + boolean pending = changeSet.getType() == ChangeSetType.PENDING_DROPS; + if (pending) { + // un-applied drop columns etc + pendingDrops.add(version, changeSet); + } else if (isDropsFor(changeSet)) { + // applied drops (so no longer pending) + pendingDrops.remove(MigrationVersion.parse(changeSet.getDropsFor())); + } + if (!isDropsFor(changeSet)) { + applyChangeSet(changeSet); + } } } + /** + * Return true if the changeSet contains drops for a previous PENDING_DROPS changeSet. + */ + private boolean isDropsFor(ChangeSet changeSet) { + return changeSet.getDropsFor() != null; + } + /** * Apply a changeSet to the model. */ @@ -245,4 +264,44 @@ public class ModelContainer { indexes.put(indexName, new MIndex(indexName, tableName, columnNames)); } + + /** + * Return true if there are pending drops. + */ + public boolean hasPendingDrops() { + return !pendingDrops.isEmpty(); + } + + /** + * Return the list of versions containing un-applied pending drops. + */ + public List getPendingDrops() { + return pendingDrops.pendingDrops(); + } + + /** + * Return the migration for the pending drops for a given version. + */ + public Migration migrationForPendingDrop(String pendingVersion) { + return pendingDrops.migrationForVersion(pendingVersion); + } + + /** + * Register the drop columns on history tables that have not been applied yet. + */ + public void registerPendingHistoryDropColumns(ModelContainer newModel) { + pendingDrops.registerPendingHistoryDropColumns(newModel); + } + + /** + * Register a drop column on a history tables that has not been applied yet. + */ + public void registerPendingDropColumn(DropColumn dropColumn) { + + MTable table = getTable(dropColumn.getTableName()); + if (table == null) { + throw new IllegalArgumentException("Table ["+dropColumn.getTableName()+"] not found?"); + } + table.registerPendingDropColumn(dropColumn.getColumnName()); + } } diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java index a0214661c..1f7d42486 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelDiff.java @@ -111,7 +111,7 @@ public class ModelDiff { public ChangeSet getDropChangeSet() { // put the changes into a ChangeSet ChangeSet createChangeSet = new ChangeSet(); - createChangeSet.setType(ChangeSetType.DROP); + createChangeSet.setType(ChangeSetType.PENDING_DROPS); createChangeSet.getChangeSetChildren().addAll(dropChanges); return createChangeSet; } @@ -156,6 +156,7 @@ public class ModelDiff { } } + baseModel.registerPendingHistoryDropColumns(newModel); } protected void addDropTable(MTable existingTable) { diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/PendingDrops.java b/src/main/java/com/avaje/ebean/dbmigration/model/PendingDrops.java new file mode 100644 index 000000000..4c0f44804 --- /dev/null +++ b/src/main/java/com/avaje/ebean/dbmigration/model/PendingDrops.java @@ -0,0 +1,131 @@ +package com.avaje.ebean.dbmigration.model; + +import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.ChangeSetType; +import com.avaje.ebean.dbmigration.migration.DropColumn; +import com.avaje.ebean.dbmigration.migration.Migration; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; + +/** + * The migrations with pending un-applied drops. + */ +public class PendingDrops { + + private final LinkedHashMap map = new LinkedHashMap(); + + /** + * Add a 'pending drops' changeSet for the given version. + */ + public void add(MigrationVersion version, ChangeSet changeSet) { + + Entry entry = map.get(version.normalised()); + if (entry == null) { + entry = new Entry(version); + map.put(version.normalised(), entry); + } + entry.add(changeSet); + } + + /** + * Return the list of versions with pending drops. + */ + public List pendingDrops() { + + List versions = new ArrayList(); + for (Entry value : map.values()) { + versions.add(value.version.asString()); + } + return versions; + } + + /** + * Remove the pending drops for a version (as they have been applied). + */ + public void remove(MigrationVersion version) { + map.remove(version.normalised()); + } + + /** + * Return true if there are no pending drops. + */ + public boolean isEmpty() { + return map.isEmpty(); + } + + /** + * Return the migration for the pending drops from a version. + *

+ * The value of version can be "next" to find the first un-applied pending drops. + *

+ */ + public Migration migrationForVersion(String pendingVersion) { + + Entry entry = getChangeSets(pendingVersion); + + Migration migration = new Migration(); + for (ChangeSet changeSet : entry.list) { + changeSet.setType(ChangeSetType.APPLY); + changeSet.setDropsFor(entry.version.asString()); + migration.getChangeSet().add(changeSet); + } + + return migration; + } + + private Entry getChangeSets(String pendingVersion) { + + if ("next".equalsIgnoreCase(pendingVersion)) { + Iterator it = map.values().iterator(); + if (it.hasNext()) { + Entry first = it.next(); + it.remove(); + return first; + } + } else { + Entry remove = map.remove(MigrationVersion.parse(pendingVersion).normalised()); + if (remove != null) { + return remove; + } + } + throw new IllegalArgumentException("No pending changeSets for version [" + pendingVersion + "] found"); + } + + /** + * Register pending drop columns on history tables to the new model. + */ + public void registerPendingHistoryDropColumns(ModelContainer newModel) { + + for (Entry entry : map.values()) { + for (ChangeSet changeSet : entry.list) { + for (Object change : changeSet.getChangeSetChildren()) { + if (change instanceof DropColumn) { + DropColumn dropColumn = (DropColumn) change; + if (Boolean.TRUE.equals(dropColumn.isWithHistory())) { + newModel.registerPendingDropColumn(dropColumn); + } + } + } + } + } + } + + static class Entry { + + final MigrationVersion version; + + final List list = new ArrayList(); + + Entry(MigrationVersion version) { + this.version = version; + } + + void add(ChangeSet changeSet) { + list.add(changeSet); + } + } + +} diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/PlatformDdlWriter.java b/src/main/java/com/avaje/ebean/dbmigration/model/PlatformDdlWriter.java index ad6dae4e6..80aae772b 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/PlatformDdlWriter.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/PlatformDdlWriter.java @@ -7,6 +7,7 @@ import com.avaje.ebean.dbmigration.ddlgeneration.DdlBuffer; import com.avaje.ebean.dbmigration.ddlgeneration.DdlHandler; import com.avaje.ebean.dbmigration.ddlgeneration.DdlWrite; import com.avaje.ebean.dbmigration.migration.ChangeSet; +import com.avaje.ebean.dbmigration.migration.ChangeSetType; import com.avaje.ebean.dbmigration.migration.Migration; import java.io.File; @@ -44,7 +45,7 @@ public class PlatformDdlWriter { List changeSets = dbMigration.getChangeSet(); for (ChangeSet changeSet : changeSets) { - if (!changeSet.getChangeSetChildren().isEmpty()) { + if (isApply(changeSet)) { handler.generate(write, changeSet); } } @@ -53,6 +54,13 @@ public class PlatformDdlWriter { writePlatformDdl(write, writePath, fullVersion); } + /** + * Return true if the changeSet is APPLY and not empty. + */ + private boolean isApply(ChangeSet changeSet) { + return changeSet.getType() == ChangeSetType.APPLY && !changeSet.getChangeSetChildren().isEmpty(); + } + /** * Write the ddl files. */ @@ -78,15 +86,6 @@ public class PlatformDdlWriter { } } - if (!write.isDropEmpty()) { - FileWriter dropWriter = createWriter(resourcePath, fullVersion, config.getDropPath(), config.getDropSuffix()); - try { - writeDropDdl(dropWriter, write); - dropWriter.flush(); - } finally { - dropWriter.close(); - } - } } protected FileWriter createWriter(File path, String fullVersion, String subPath, String suffix) throws IOException { @@ -136,17 +135,6 @@ public class PlatformDdlWriter { writer.append(write.rollback().getBuffer()); } - /** - * Write the 'Drop' DDL buffers to the writer. - */ - protected void writeDropDdl(Writer writer, DdlWrite write) throws IOException { - - // merge the rollback buffers in the appropriate order - prependDropDependencies(writer, write.dropDropDependencies()); - writer.append(write.dropHistory().getBuffer()); - writer.append(write.drop().getBuffer()); - } - private void prependDropDependencies(Writer writer, DdlBuffer buffer) throws IOException { if (!buffer.isEmpty()) { writer.append("-- drop dependencies\n"); diff --git a/src/main/resources/ebean-dbmigration-1.0.xsd b/src/main/resources/ebean-dbmigration-1.0.xsd index d33e3570b..ab0b7bf08 100644 --- a/src/main/resources/ebean-dbmigration-1.0.xsd +++ b/src/main/resources/ebean-dbmigration-1.0.xsd @@ -24,6 +24,7 @@ + @@ -33,7 +34,7 @@ - + diff --git a/src/test/java/FooTest.java b/src/test/java/FooTest.java new file mode 100644 index 000000000..a3963f806 --- /dev/null +++ b/src/test/java/FooTest.java @@ -0,0 +1,37 @@ +/** + * Created by rob on 11/02/16. + */ +public class FooTest {// + + interface Colour { + int RED = 1; + } + + static class SuperTest { + int RED = 999; + } + + static class Test extends SuperTest implements Colour { + + String RED = "RED"; + + public static void main(String[] args) { + new Test().printV(); + } + + void printV() { + System.out.println(super.RED + " " + this.RED + " " + RED + " " + Colour.RED); + } + } + +// static class Test3 { +// public static void main(String[] args) { +// new Test().printV(); +// } +// +// void printV2() { +// System.out.println(Colour.order); +// } +// +// } +} diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/H2HistoryDdlTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/H2HistoryDdlTest.java index 72d5c982f..c996bf61a 100644 --- a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/H2HistoryDdlTest.java +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/H2HistoryDdlTest.java @@ -33,9 +33,6 @@ public class H2HistoryDdlTest { h2Ddl.configure(ebeanServer.getServerConfig()); h2Ddl.regenerateHistoryTriggers(write, update); - assertThat(write.dropHistory().isEmpty()).isFalse(); - assertThat(write.dropHistory().getBuffer()).contains("drop two"); - assertThat(write.applyHistory().isEmpty()).isFalse(); assertThat(write.applyHistory().getBuffer()).contains("add one"); assertThat(write.applyHistory().getBuffer()).doesNotContain("two"); diff --git a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/HistoryTableUpdateTest.java b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/HistoryTableUpdateTest.java index 3641a78e0..24ed28c23 100644 --- a/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/HistoryTableUpdateTest.java +++ b/src/test/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/HistoryTableUpdateTest.java @@ -17,8 +17,6 @@ public class HistoryTableUpdateTest { assertThat(upd.getBaseTable()).isEqualTo("mytab"); upd.add(HistoryTableUpdate.Change.ADD, "two"); - assertThat(upd.hasApplyChanges()).isTrue(); - assertThat(upd.hasDropChanges()).isFalse(); List current = current(); upd.toRevertedColumns(current); @@ -30,8 +28,6 @@ public class HistoryTableUpdateTest { HistoryTableUpdate upd = new HistoryTableUpdate("mytab"); upd.add(HistoryTableUpdate.Change.INCLUDE, "two"); - assertThat(upd.hasApplyChanges()).isTrue(); - assertThat(upd.hasDropChanges()).isFalse(); List current = current(); upd.toRevertedColumns(current); @@ -43,8 +39,6 @@ public class HistoryTableUpdateTest { HistoryTableUpdate upd = new HistoryTableUpdate("mytab"); upd.add(HistoryTableUpdate.Change.DROP, "three"); - assertThat(upd.hasApplyChanges()).isFalse(); - assertThat(upd.hasDropChanges()).isTrue(); List current = current(); upd.toRevertedColumns(current); @@ -56,8 +50,6 @@ public class HistoryTableUpdateTest { HistoryTableUpdate upd = new HistoryTableUpdate("mytab"); upd.add(HistoryTableUpdate.Change.EXCLUDE, "four"); - assertThat(upd.hasApplyChanges()).isTrue(); - assertThat(upd.hasDropChanges()).isFalse(); List current = current(); upd.toRevertedColumns(current); @@ -71,11 +63,8 @@ public class HistoryTableUpdateTest { HistoryTableUpdate upd = new HistoryTableUpdate("mytab"); upd.add(HistoryTableUpdate.Change.ADD, "two"); upd.add(HistoryTableUpdate.Change.DROP, "four"); - assertThat(upd.hasApplyChanges()).isTrue(); - assertThat(upd.hasDropChanges()).isTrue(); - assertThat(upd.descriptionForApply()).isEqualTo("add two"); - assertThat(upd.descriptionForDrop()).isEqualTo("drop four"); + assertThat(upd.description()).isEqualTo("add two"); } @Test @@ -86,11 +75,8 @@ public class HistoryTableUpdateTest { upd.add(HistoryTableUpdate.Change.INCLUDE, "five"); upd.add(HistoryTableUpdate.Change.EXCLUDE, "six"); upd.add(HistoryTableUpdate.Change.DROP, "four"); - assertThat(upd.hasApplyChanges()).isTrue(); - assertThat(upd.hasDropChanges()).isTrue(); - assertThat(upd.descriptionForApply()).isEqualTo("add two, include five, exclude six"); - assertThat(upd.descriptionForDrop()).isEqualTo("drop four"); + assertThat(upd.description()).isEqualTo("add two, include five, exclude six"); } List current() { diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/MTableTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/MTableTest.java index ac22ba0a4..d327f7a74 100644 --- a/src/test/java/com/avaje/ebean/dbmigration/model/MTableTest.java +++ b/src/test/java/com/avaje/ebean/dbmigration/model/MTableTest.java @@ -57,11 +57,11 @@ public class MTableTest { public void test_allHistoryColumns() throws Exception { MTable base = base(); - base.registerDroppedColumn("fullName",2); - base.registerDroppedColumn("last",4); + base.registerPendingDropColumn("fullName"); + base.registerPendingDropColumn("last"); assertThat(base.allHistoryColumns(false)).containsExactly("id","name","status"); - assertThat(base.allHistoryColumns(true)).containsExactly("id","name","fullName","status","last"); + assertThat(base.allHistoryColumns(true)).containsExactly("id","name","status","fullName","last"); } @Test diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/MigrationVersionTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/MigrationVersionTest.java index 214bf41db..08e807a6f 100644 --- a/src/test/java/com/avaje/ebean/dbmigration/model/MigrationVersionTest.java +++ b/src/test/java/com/avaje/ebean/dbmigration/model/MigrationVersionTest.java @@ -6,8 +6,47 @@ import static org.assertj.core.api.StrictAssertions.assertThat; public class MigrationVersionTest { + + @Test - public void testParse() throws Exception { + public void test_parse_getComment() throws Exception { + + assertThat(MigrationVersion.parse("1.1.1_2__Foo").getComment()).isEqualTo("Foo"); + assertThat(MigrationVersion.parse("1.1.1.2__junk").getComment()).isEqualTo("junk"); + assertThat(MigrationVersion.parse("1.1_1.2_foo").getComment()).isEqualTo(""); + assertThat(MigrationVersion.parse("1.1_1.2_d").getComment()).isEqualTo(""); + assertThat(MigrationVersion.parse("1.1_1.2_").getComment()).isEqualTo(""); + assertThat(MigrationVersion.parse("1.1_1.2").getComment()).isEqualTo(""); + } + + @Test + public void test_nextVersion_expect_preserveUnderscores() { + + assertThat(MigrationVersion.parse("2").nextVersion()).isEqualTo("3"); + assertThat(MigrationVersion.parse("1.0").nextVersion()).isEqualTo("1.1"); + assertThat(MigrationVersion.parse("2.0.b34").nextVersion()).isEqualTo("2.1"); + assertThat(MigrationVersion.parse("1.1.1_2__Foo").nextVersion()).isEqualTo("1.1.1_3"); + assertThat(MigrationVersion.parse("1.1.1.2_junk").nextVersion()).isEqualTo("1.1.1.3"); + assertThat(MigrationVersion.parse("1_2.3_4__Foo").nextVersion()).isEqualTo("1_2.3_5"); + assertThat(MigrationVersion.parse("1_2.3_4_").nextVersion()).isEqualTo("1_2.3_5"); + assertThat(MigrationVersion.parse("1_2_3_4__Foo").nextVersion()).isEqualTo("1_2_3_5"); + } + + @Test + public void test_normalised_expect_periods() { + + assertThat(MigrationVersion.parse("2").normalised()).isEqualTo("2"); + assertThat(MigrationVersion.parse("1.0").normalised()).isEqualTo("1.0"); + assertThat(MigrationVersion.parse("2.0.b34").normalised()).isEqualTo("2.0"); + assertThat(MigrationVersion.parse("1.1.1_2__Foo").normalised()).isEqualTo("1.1.1.2"); + assertThat(MigrationVersion.parse("1.1.1.2_junk").normalised()).isEqualTo("1.1.1.2"); + assertThat(MigrationVersion.parse("1_2.3_4__Foo").normalised()).isEqualTo("1.2.3.4"); + assertThat(MigrationVersion.parse("1_2.3_4_").normalised()).isEqualTo("1.2.3.4"); + assertThat(MigrationVersion.parse("1_2_3_4__Foo").normalised()).isEqualTo("1.2.3.4"); + } + + @Test + public void test_compareTo_isEqual() throws Exception { MigrationVersion v0 = MigrationVersion.parse("1.1.1_2__Foo"); MigrationVersion v1 = MigrationVersion.parse("1.1.1.2_junk"); @@ -16,32 +55,21 @@ public class MigrationVersionTest { assertThat(v0.compareTo(v1)).isEqualTo(0); assertThat(v1.compareTo(v0)).isEqualTo(0); assertThat(v1.compareTo(v2)).isEqualTo(0); - } @Test - public void testNextVersion() { - - assertThat(MigrationVersion.parse("2").nextVersion()).isEqualTo("3"); - assertThat(MigrationVersion.parse("1.0").nextVersion()).isEqualTo("1.1"); - assertThat(MigrationVersion.parse("2.0.b34").nextVersion()).isEqualTo("2.1"); - assertThat(MigrationVersion.parse("1.1.1_2__Foo").nextVersion()).isEqualTo("1.1.1.3"); - assertThat(MigrationVersion.parse("1.1.1.2_junk").nextVersion()).isEqualTo("1.1.1.3"); - } - - @Test - public void testCompareTo() throws Exception { + public void test_compareTo() throws Exception { + MigrationVersion v0 = MigrationVersion.parse("1.1.1.1_junk"); MigrationVersion v1 = MigrationVersion.parse("1.1.1.2_junk"); - MigrationVersion v2 = MigrationVersion.parse("2.1_1.2_junk"); + MigrationVersion v2 = MigrationVersion.parse("1.1_1.3_junk"); MigrationVersion v3 = MigrationVersion.parse("1.2_1.2_junk"); - MigrationVersion v4 = MigrationVersion.parse("1.1_1.3_junk"); - MigrationVersion v5 = MigrationVersion.parse("1.1.1.1_junk"); + MigrationVersion v4 = MigrationVersion.parse("2.1_1.2_junk"); + + assertThat(v1.compareTo(v0)).isEqualTo(1); assertThat(v1.compareTo(v2)).isEqualTo(-1); assertThat(v1.compareTo(v3)).isEqualTo(-1); assertThat(v1.compareTo(v4)).isEqualTo(-1); - - assertThat(v1.compareTo(v5)).isEqualTo(1); } } \ No newline at end of file diff --git a/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java index 2562b341f..f82c563cc 100644 --- a/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java +++ b/src/test/java/com/avaje/ebean/dbmigration/model/ModelContainerApplyTest.java @@ -29,7 +29,7 @@ public class ModelContainerApplyTest { assertThat(changeSetChildren.get(2)).isInstanceOf(DropColumn.class); ModelContainer model = new ModelContainer(); - model.apply(migration); + model.apply(migration, MigrationVersion.parse("1.1")); MTable foo = model.getTable("foo"); assertThat(foo.getComment()).isEqualTo("comment");