diff --git a/ebean-migration/src/main/java/io/ebean/migration/MigrationConfig.java b/ebean-migration/src/main/java/io/ebean/migration/MigrationConfig.java index a972692..fa28208 100644 --- a/ebean-migration/src/main/java/io/ebean/migration/MigrationConfig.java +++ b/ebean-migration/src/main/java/io/ebean/migration/MigrationConfig.java @@ -65,7 +65,8 @@ public class MigrationConfig { private String basePlatform; private String platform; private Properties properties; - + private boolean earlyChecksumMode; + private boolean autoPatchChecksum = true; /** * Return the name of the migration table. */ @@ -469,6 +470,8 @@ public void load(Properties props) { dbSchema = getProperty("schema", dbSchema); skipMigrationRun = getBool("skipMigrationRun", skipMigrationRun); skipChecksum = getBool("skipChecksum", skipChecksum); + earlyChecksumMode = getBool("earlyChecksum", earlyChecksumMode); + autoPatchChecksum = getBool("autoPatchChecksum", autoPatchChecksum); createSchemaIfNotExists = getBool("createSchemaIfNotExists", createSchemaIfNotExists); setCurrentSchema = getBool("setCurrentSchema", setCurrentSchema); basePlatform = getProperty("basePlatform", basePlatform); @@ -575,6 +578,36 @@ public void setPlatform(String platform) { this.platform = platform; } + /** + * Return true if changing to use earlyChecksumMode, and we want to automatically + * patch checksums that were computed on the original mode. + */ + public boolean isAutoPatchChecksum() { + return autoPatchChecksum; + } + + /** + * Set this to false in order to turn off auto patching for earlyChecksumMode. + */ + public void setAutoPatchChecksum(boolean autoPatchChecksum) { + this.autoPatchChecksum = autoPatchChecksum; + } + + /** + * Return true if using the earlyChecksumMode which means checksums are computed + * before any expressions in the scripts are translated. + */ + public boolean isEarlyChecksumMode() { + return earlyChecksumMode; + } + + /** + * Set to true to turn on earlyChecksumMode. + */ + public void setEarlyChecksumMode(boolean earlyChecksumMode) { + this.earlyChecksumMode = earlyChecksumMode; + } + /** * Default factory. Uses the migration's class loader and injects the config if necessary. * diff --git a/ebean-migration/src/main/java/io/ebean/migration/runner/MigrationTable.java b/ebean-migration/src/main/java/io/ebean/migration/runner/MigrationTable.java index c708eae..2ee8f61 100644 --- a/ebean-migration/src/main/java/io/ebean/migration/runner/MigrationTable.java +++ b/ebean-migration/src/main/java/io/ebean/migration/runner/MigrationTable.java @@ -24,6 +24,8 @@ final class MigrationTable { private final Connection connection; private final boolean checkStateOnly; + private final boolean autoPatchChecksum; + private final boolean useEarlyChecksum; private final MigrationPlatform platform; private final MigrationScriptRunner scriptRunner; private final String catalog; @@ -74,6 +76,8 @@ final class MigrationTable { this.connection = connection; this.scriptRunner = new MigrationScriptRunner(connection, platform); this.checkStateOnly = checkStateOnly; + this.useEarlyChecksum = config.isEarlyChecksumMode(); + this.autoPatchChecksum = !checkStateOnly && config.isAutoPatchChecksum(); this.migrations = new LinkedHashMap<>(); this.catalog = null; this.allowErrorInRepeatable = config.isAllowErrorInRepeatable(); @@ -296,12 +300,16 @@ private boolean shouldRun(LocalMigrationResource localVersion, LocalMigrationRes private boolean runMigration(LocalMigrationResource local, MigrationMetaRow existing) throws SQLException { String script = null; int checksum; + int checksum2 = 0; if (local instanceof LocalUriMigrationResource) { - script = convertScript(local.content()); checksum = ((LocalUriMigrationResource)local).checksum(); - } else if (local instanceof LocalDdlMigrationResource) { script = convertScript(local.content()); - checksum = Checksum.calculate(script); + } else if (local instanceof LocalDdlMigrationResource) { + final String content = local.content(); + script = convertScript(content); + // checksum on original content (NEW) or converted script content (LEGACY) + checksum = Checksum.calculate(useEarlyChecksum ? content : script); + checksum2 = autoPatchChecksum ? Checksum.calculate(script) : 0; } else { checksum = ((LocalJdbcMigrationResource) local).checksum(); } @@ -309,7 +317,7 @@ private boolean runMigration(LocalMigrationResource local, MigrationMetaRow exis if (existing == null && patchInsertMigration(local, checksum)) { return true; } - if (existing != null && skipMigration(checksum, local, existing)) { + if (existing != null && skipMigration(checksum, checksum2, local, existing)) { return true; } executeMigration(local, script, checksum, existing); @@ -333,12 +341,17 @@ private boolean patchInsertMigration(LocalMigrationResource local, int checksum) /** * Return true if the migration should be skipped. */ - boolean skipMigration(int checksum, LocalMigrationResource local, MigrationMetaRow existing) throws SQLException { + boolean skipMigration(int checksum, int checksum2, LocalMigrationResource local, MigrationMetaRow existing) throws SQLException { boolean matchChecksum = (existing.checksum() == checksum); if (matchChecksum) { log.log(TRACE, "skip unchanged migration {0}", local.location()); return true; + } else if (autoPatchChecksum && existing.checksum() == checksum2) { + log.log(INFO, "Patch migration, reset checksum on {0}", local.location()); + existing.resetChecksum(checksum, connection, updateChecksumSql); + return true; + } else if (patchResetChecksum(existing, checksum)) { log.log(INFO, "Patch migration, reset checksum on {0}", local.location()); return true; diff --git a/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTable2Test.java b/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTable2Test.java index ecaab03..2e2fbbd 100644 --- a/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTable2Test.java +++ b/ebean-migration/src/test/java/io/ebean/migration/runner/MigrationTable2Test.java @@ -48,9 +48,9 @@ void test_skipMigration_Repeatable() throws Exception { MigrationTable mt = new MigrationTable(config, null, false, new MigrationPlatform()); // checksum different - no skip - assertFalse(mt.skipMigration(100, local, existing)); + assertFalse(mt.skipMigration(100, 100, local, existing)); // checksum same - skip - assertTrue(mt.skipMigration(42, local, existing)); + assertTrue(mt.skipMigration(42, 42, local, existing)); } @Test @@ -58,6 +58,7 @@ void test_skipMigration_skipChecksum() throws Exception { MigrationConfig config = new MigrationConfig(); config.setSkipChecksum(true); + config.setAutoPatchChecksum(true); MigrationTable mt = new MigrationTable(config, null, false, new MigrationPlatform()); @@ -66,19 +67,20 @@ void test_skipMigration_skipChecksum() throws Exception { MigrationMetaRow existing = new MigrationMetaRow(12, "R", "", "comment", 42, null, null, 0); // repeatable checksum mismatch - assertFalse(mt.skipMigration(44, local, existing)); + assertFalse(mt.skipMigration(44, 44, local, existing)); // repeatable match checksum - assertTrue(mt.skipMigration(42, local, existing)); + assertTrue(mt.skipMigration(42, 42, local, existing)); + // assertTrue(mt.skipMigration(99, 42, local, existing)); LocalMigrationResource localVer = local("V1__hello"); MigrationMetaRow localExisting = new MigrationMetaRow(12, "V", "1", "comment", 42, null, null, 0); // re-run on checksum mismatch and skipChecksum - assertFalse(mt.skipMigration(44, localVer, localExisting)); + assertFalse(mt.skipMigration(44, 44, localVer, localExisting)); // match checksum so skip - assertTrue(mt.skipMigration(42, localVer, localExisting)); + assertTrue(mt.skipMigration(42, 42, localVer, localExisting)); } private LocalMigrationResource local(String raw) {