From a71dea8e42839dc1cf54bf8dc85ecb80086c5dcd Mon Sep 17 00:00:00 2001 From: Federico Fissore Date: Thu, 20 Jun 2019 11:18:28 +0200 Subject: [PATCH 1/5] Status operation now merges the list of migrations found of disk with those stored on db, and it also looks for migrations which are locally missing but have been applied on db. A missing migration is thus a migration applied by someone else (eg: another developer) and locally missing. Output of `migrate status` will look like ID Applied At Description ================================================================================ 20190620071710 2019-06-20 09:41:51 create changelog 20190620071711 2019-06-20 09:41:51 first migration 20190620074320 ...missing... migration1 20190620085416 2019-06-20 10:54:19 migration2 --- .../org/apache/ibatis/migration/Change.java | 22 +++++++++++++- .../migration/operations/StatusOperation.java | 27 +++++++++++------ .../RuntimeMigrationTest.java | 27 +++++++++++++++-- .../20130707120740_create_third_table.sql | 29 +++++++++++++++++++ 4 files changed, 92 insertions(+), 13 deletions(-) create mode 100644 src/test/java/org/apache/ibatis/migration/runtime_migration/scripts_from_other_branch/20130707120740_create_third_table.sql diff --git a/src/main/java/org/apache/ibatis/migration/Change.java b/src/main/java/org/apache/ibatis/migration/Change.java index bc817513..2e527e49 100644 --- a/src/main/java/org/apache/ibatis/migration/Change.java +++ b/src/main/java/org/apache/ibatis/migration/Change.java @@ -22,6 +22,7 @@ public class Change implements Comparable, Cloneable { private BigDecimal id; private String description; private String appliedTimestamp; + private boolean missing; private String filename; public Change() { @@ -61,6 +62,14 @@ public void setAppliedTimestamp(String appliedTimestamp) { this.appliedTimestamp = appliedTimestamp; } + public boolean isApplied() { + return getAppliedTimestamp() != null; + } + + public void setMissing(boolean missing) { + this.missing = missing; + } + public String getFilename() { return filename; } @@ -71,7 +80,18 @@ public void setFilename(String filename) { @Override public String toString() { - return id + " " + (appliedTimestamp == null ? " ...pending... " : appliedTimestamp) + " " + description; + StringBuilder sb = new StringBuilder(); + sb.append(id).append(" "); + if (missing) { + sb.append(" ...missing... "); + } else if (appliedTimestamp == null) { + sb.append(" ...pending... "); + } else { + sb.append(appliedTimestamp); + } + sb.append(" ").append(description); + + return sb.toString(); } @Override diff --git a/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java b/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java index 66c65385..33257f7a 100644 --- a/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java +++ b/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java @@ -16,9 +16,7 @@ package org.apache.ibatis.migration.operations; import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; import org.apache.ibatis.migration.Change; import org.apache.ibatis.migration.ConnectionProvider; @@ -27,9 +25,10 @@ import org.apache.ibatis.migration.utils.Util; public final class StatusOperation extends DatabaseOperation { - private int applied; + private int applied; private int pending; + private int missing; private List changes; @@ -44,13 +43,19 @@ public StatusOperation operate(ConnectionProvider connectionProvider, MigrationL List migrations = migrationsLoader.getMigrations(); if (changelogExists(connectionProvider, option)) { List changelog = getChangelog(connectionProvider, option); - for (Change change : migrations) { - int index = changelog.indexOf(change); - if (index > -1) { - changes.add(changelog.get(index)); + + Set changelogAndMigrations = new HashSet(); + changelogAndMigrations.addAll(changelog); + changelogAndMigrations.addAll(migrations); + + for (Change change : changelogAndMigrations) { + changes.add(change); + if (!migrations.contains(change)) { + change.setMissing(true); + missing++; + } else if (change.isApplied()) { applied++; } else { - changes.add(change); pending++; } } @@ -74,6 +79,10 @@ public int getPendingCount() { return pending; } + public int getMissingCount() { + return missing; + } + public List getCurrentStatus() { return changes; } diff --git a/src/test/java/org/apache/ibatis/migration/runtime_migration/RuntimeMigrationTest.java b/src/test/java/org/apache/ibatis/migration/runtime_migration/RuntimeMigrationTest.java index 48c49e47..d1833af7 100644 --- a/src/test/java/org/apache/ibatis/migration/runtime_migration/RuntimeMigrationTest.java +++ b/src/test/java/org/apache/ibatis/migration/runtime_migration/RuntimeMigrationTest.java @@ -56,12 +56,16 @@ public class RuntimeMigrationTest { private MigrationLoader migrationsLoader; + private MigrationLoader migrationsLoaderFromOtherBranch; + @Before public void setup() throws Exception { connectionProvider = new JdbcConnectionProvider("org.hsqldb.jdbcDriver", "jdbc:hsqldb:mem:javaapitest", "sa", ""); dbOption = new DatabaseOperationOption(); out = new ByteArrayOutputStream(); - migrationsLoader = createMigrationsLoader(); + migrationsLoader = createMigrationsLoader("org/apache/ibatis/migration/runtime_migration/scripts"); + migrationsLoaderFromOtherBranch = createMigrationsLoader( + "org/apache/ibatis/migration/runtime_migration/scripts_from_other_branch"); } @After @@ -106,6 +110,23 @@ public void testUp() throws Exception { assertEquals(3, status.getCurrentStatus().size()); } + @Test + public void testUpFromDifferentBranches() throws Exception { + new UpOperation().operate(connectionProvider, migrationsLoader, dbOption, new PrintStream(out)); + new UpOperation().operate(connectionProvider, migrationsLoaderFromOtherBranch, dbOption, new PrintStream(out)); + assertEquals("4", runQuery(connectionProvider, "select count(*) from changelog")); + assertEquals("0", runQuery(connectionProvider, "select count(*) from first_table")); + assertEquals("0", runQuery(connectionProvider, "select count(*) from second_table")); + assertEquals("0", runQuery(connectionProvider, "select count(*) from third_table")); + + StatusOperation status = new StatusOperation().operate(connectionProvider, migrationsLoader, dbOption, + new PrintStream(out)); + assertEquals(3, status.getAppliedCount()); + assertEquals(0, status.getPendingCount()); + assertEquals(1, status.getMissingCount()); + assertEquals(4, status.getCurrentStatus().size()); + } + @Test public void testUpWithStep() throws Exception { new UpOperation(2).operate(connectionProvider, migrationsLoader, dbOption, new PrintStream(out)); @@ -217,8 +238,8 @@ protected void assertTableDoesNotExist(ConnectionProvider connectionProvider, St } } - protected FileMigrationLoader createMigrationsLoader() { - URL url = getClass().getClassLoader().getResource("org/apache/ibatis/migration/runtime_migration/scripts"); + protected FileMigrationLoader createMigrationsLoader(String resource) { + URL url = getClass().getClassLoader().getResource(resource); File scriptsDir = new File(url.getFile()); Properties properties = new Properties(); properties.setProperty("changelog", "CHANGELOG"); diff --git a/src/test/java/org/apache/ibatis/migration/runtime_migration/scripts_from_other_branch/20130707120740_create_third_table.sql b/src/test/java/org/apache/ibatis/migration/runtime_migration/scripts_from_other_branch/20130707120740_create_third_table.sql new file mode 100644 index 00000000..4824612c --- /dev/null +++ b/src/test/java/org/apache/ibatis/migration/runtime_migration/scripts_from_other_branch/20130707120740_create_third_table.sql @@ -0,0 +1,29 @@ +-- +-- Copyright 2010-2019 the original author or authors. +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +-- // Second migration. +-- Migration SQL that makes the change goes here. + +CREATE TABLE third_table ( +ID INTEGER NOT NULL, +NAME VARCHAR(16) +); + + +-- //@UNDO +-- SQL to undo the change goes here. + +DROP TABLE third_table; From 10947101754afc8e514b1a3f31cf5d9ebdf47bd0 Mon Sep 17 00:00:00 2001 From: Federico Fissore Date: Thu, 20 Jun 2019 11:18:51 +0200 Subject: [PATCH 2/5] Changes generated by the build process --- Dockerfile | 16 ++++++++++++++++ .../java/org/apache/ibatis/migration/Change.java | 2 +- .../migration/operations/StatusOperation.java | 2 +- .../runtime_migration/RuntimeMigrationTest.java | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index e38062cd..adcba46f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,19 @@ +# +# Copyright 2010-2019 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + FROM openjdk:8 RUN ["mkdir", "-p", "/opt/migrations"] diff --git a/src/main/java/org/apache/ibatis/migration/Change.java b/src/main/java/org/apache/ibatis/migration/Change.java index 2e527e49..ff2779f5 100644 --- a/src/main/java/org/apache/ibatis/migration/Change.java +++ b/src/main/java/org/apache/ibatis/migration/Change.java @@ -1,5 +1,5 @@ /** - * Copyright 2010-2017 the original author or authors. + * Copyright 2010-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java b/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java index 33257f7a..7c077381 100644 --- a/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java +++ b/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java @@ -1,5 +1,5 @@ /** - * Copyright 2010-2017 the original author or authors. + * Copyright 2010-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/apache/ibatis/migration/runtime_migration/RuntimeMigrationTest.java b/src/test/java/org/apache/ibatis/migration/runtime_migration/RuntimeMigrationTest.java index d1833af7..16e7e200 100644 --- a/src/test/java/org/apache/ibatis/migration/runtime_migration/RuntimeMigrationTest.java +++ b/src/test/java/org/apache/ibatis/migration/runtime_migration/RuntimeMigrationTest.java @@ -1,5 +1,5 @@ /** - * Copyright 2010-2017 the original author or authors. + * Copyright 2010-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 140a0f31bf21a0a10fcd930ddf4dbbe402fe14ea Mon Sep 17 00:00:00 2001 From: Federico Fissore Date: Fri, 21 Jun 2019 09:28:40 +0200 Subject: [PATCH 3/5] Addressed feedback on StatusOperation: reverted Change to previous version, added MissingScript to mark a Change as missing, added output of `checkSkippedOrMissing` to the bottom of `status` command --- .../org/apache/ibatis/migration/Change.java | 27 ++++-------------- .../ibatis/migration/MissingScript.java | 28 +++++++++++++++++++ .../migration/operations/StatusOperation.java | 16 ++++++++--- 3 files changed, 46 insertions(+), 25 deletions(-) create mode 100644 src/main/java/org/apache/ibatis/migration/MissingScript.java diff --git a/src/main/java/org/apache/ibatis/migration/Change.java b/src/main/java/org/apache/ibatis/migration/Change.java index ff2779f5..dbee63e4 100644 --- a/src/main/java/org/apache/ibatis/migration/Change.java +++ b/src/main/java/org/apache/ibatis/migration/Change.java @@ -22,7 +22,6 @@ public class Change implements Comparable, Cloneable { private BigDecimal id; private String description; private String appliedTimestamp; - private boolean missing; private String filename; public Change() { @@ -38,6 +37,11 @@ public Change(BigDecimal id, String appliedTimestamp, String description) { this.description = description; } + protected Change(Change toCopy) { + this(toCopy.getId(), toCopy.getAppliedTimestamp(), toCopy.getDescription()); + this.filename = toCopy.getFilename(); + } + public BigDecimal getId() { return id; } @@ -62,14 +66,6 @@ public void setAppliedTimestamp(String appliedTimestamp) { this.appliedTimestamp = appliedTimestamp; } - public boolean isApplied() { - return getAppliedTimestamp() != null; - } - - public void setMissing(boolean missing) { - this.missing = missing; - } - public String getFilename() { return filename; } @@ -80,18 +76,7 @@ public void setFilename(String filename) { @Override public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(id).append(" "); - if (missing) { - sb.append(" ...missing... "); - } else if (appliedTimestamp == null) { - sb.append(" ...pending... "); - } else { - sb.append(appliedTimestamp); - } - sb.append(" ").append(description); - - return sb.toString(); + return id + " " + (appliedTimestamp == null ? " ...pending... " : appliedTimestamp) + " " + description; } @Override diff --git a/src/main/java/org/apache/ibatis/migration/MissingScript.java b/src/main/java/org/apache/ibatis/migration/MissingScript.java new file mode 100644 index 00000000..99c9684a --- /dev/null +++ b/src/main/java/org/apache/ibatis/migration/MissingScript.java @@ -0,0 +1,28 @@ +/** + * Copyright 2010-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ibatis.migration; + +public class MissingScript extends Change { + + public MissingScript(Change change) { + super(change); + } + + @Override + public String toString() { + return super.toString() + " <=== MISSING!"; + } +} diff --git a/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java b/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java index 7c077381..3f5c2da5 100644 --- a/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java +++ b/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java @@ -21,6 +21,7 @@ import org.apache.ibatis.migration.Change; import org.apache.ibatis.migration.ConnectionProvider; import org.apache.ibatis.migration.MigrationLoader; +import org.apache.ibatis.migration.MissingScript; import org.apache.ibatis.migration.options.DatabaseOperationOption; import org.apache.ibatis.migration.utils.Util; @@ -41,33 +42,40 @@ public StatusOperation operate(ConnectionProvider connectionProvider, MigrationL println(printStream, Util.horizontalLine("", 80)); changes = new ArrayList(); List migrations = migrationsLoader.getMigrations(); + List changelog = null; if (changelogExists(connectionProvider, option)) { - List changelog = getChangelog(connectionProvider, option); + changelog = getChangelog(connectionProvider, option); Set changelogAndMigrations = new HashSet(); changelogAndMigrations.addAll(changelog); changelogAndMigrations.addAll(migrations); for (Change change : changelogAndMigrations) { - changes.add(change); if (!migrations.contains(change)) { - change.setMissing(true); + change = new MissingScript(change); missing++; - } else if (change.isApplied()) { + } else if (change.getAppliedTimestamp() != null) { applied++; } else { pending++; } + changes.add(change); } } else { changes.addAll(migrations); pending = migrations.size(); } + Collections.sort(changes); for (Change change : changes) { println(printStream, change.toString()); } println(printStream); + + if (changelog != null) { + checkSkippedOrMissing(changelog, migrations, printStream); + } + return this; } From 987e936e1fd535bd8193d3f9a06f9fbc0bfe4b86 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sat, 22 Jun 2019 02:14:26 +0900 Subject: [PATCH 4/5] Use explict imports --- .../apache/ibatis/migration/operations/StatusOperation.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java b/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java index 3f5c2da5..fe708ea6 100644 --- a/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java +++ b/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java @@ -16,7 +16,11 @@ package org.apache.ibatis.migration.operations; import java.io.PrintStream; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import org.apache.ibatis.migration.Change; import org.apache.ibatis.migration.ConnectionProvider; From a039770406d5b965676d3b302ab124157fe02e26 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sat, 22 Jun 2019 02:19:59 +0900 Subject: [PATCH 5/5] Declare MissingScript in StatusOperation (for now) --- .../ibatis/migration/MissingScript.java | 28 ------------------- .../migration/operations/StatusOperation.java | 12 +++++++- 2 files changed, 11 insertions(+), 29 deletions(-) delete mode 100644 src/main/java/org/apache/ibatis/migration/MissingScript.java diff --git a/src/main/java/org/apache/ibatis/migration/MissingScript.java b/src/main/java/org/apache/ibatis/migration/MissingScript.java deleted file mode 100644 index 99c9684a..00000000 --- a/src/main/java/org/apache/ibatis/migration/MissingScript.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright 2010-2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.ibatis.migration; - -public class MissingScript extends Change { - - public MissingScript(Change change) { - super(change); - } - - @Override - public String toString() { - return super.toString() + " <=== MISSING!"; - } -} diff --git a/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java b/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java index fe708ea6..10b885b4 100644 --- a/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java +++ b/src/main/java/org/apache/ibatis/migration/operations/StatusOperation.java @@ -25,7 +25,6 @@ import org.apache.ibatis.migration.Change; import org.apache.ibatis.migration.ConnectionProvider; import org.apache.ibatis.migration.MigrationLoader; -import org.apache.ibatis.migration.MissingScript; import org.apache.ibatis.migration.options.DatabaseOperationOption; import org.apache.ibatis.migration.utils.Util; @@ -98,4 +97,15 @@ public int getMissingCount() { public List getCurrentStatus() { return changes; } + + class MissingScript extends Change { + public MissingScript(Change change) { + super(change); + } + + @Override + public String toString() { + return super.toString() + " <=== MISSING!"; + } + } }