1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.migration.operations;
17
18 import java.io.PrintStream;
19 import java.io.Reader;
20 import java.sql.Connection;
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25
26 import org.apache.ibatis.migration.Change;
27 import org.apache.ibatis.migration.ConnectionProvider;
28 import org.apache.ibatis.migration.MigrationException;
29 import org.apache.ibatis.migration.MigrationLoader;
30 import org.apache.ibatis.migration.hook.HookContext;
31 import org.apache.ibatis.migration.hook.MigrationHook;
32 import org.apache.ibatis.migration.options.DatabaseOperationOption;
33 import org.apache.ibatis.migration.utils.Util;
34
35 public final class PendingOperation extends DatabaseOperation {
36
37 public PendingOperation operate(ConnectionProvider connectionProvider, MigrationLoader migrationsLoader,
38 DatabaseOperationOption option, PrintStream printStream) {
39 return operate(connectionProvider, migrationsLoader, option, printStream, null);
40 }
41
42 public PendingOperation operate(ConnectionProvider connectionProvider, MigrationLoader migrationsLoader,
43 DatabaseOperationOption option, PrintStream printStream, MigrationHook hook) {
44 try (Connection con = connectionProvider.getConnection()) {
45 if (option == null) {
46 option = new DatabaseOperationOption();
47 }
48 if (!changelogExists(con, option)) {
49 throw new MigrationException("Change log doesn't exist, no migrations applied. Try running 'up' instead.");
50 }
51 List<Change> pending = getPendingChanges(con, migrationsLoader, option);
52 int stepCount = 0;
53 Map<String, Object> hookBindings = new HashMap<>();
54 println(printStream, "WARNING: Running pending migrations out of order can create unexpected results.");
55 try {
56 ScriptRunner runner = getScriptRunner(con, option, printStream);
57 for (Change change : pending) {
58 if (stepCount == 0 && hook != null) {
59 hookBindings.put(MigrationHook.HOOK_CONTEXT, new HookContext(connectionProvider, runner, null));
60 hook.before(hookBindings);
61 }
62 if (hook != null) {
63 hookBindings.put(MigrationHook.HOOK_CONTEXT,
64 new HookContext(connectionProvider, runner, new Change(change)));
65 hook.beforeEach(hookBindings);
66 }
67 println(printStream, Util.horizontalLine("Applying: " + change.getFilename(), 80));
68 try (Reader scriptReader = migrationsLoader.getScriptReader(change, false)) {
69 runner.runScript(scriptReader);
70 }
71 insertChangelog(change, con, option);
72 println(printStream);
73 if (hook != null) {
74 hookBindings.put(MigrationHook.HOOK_CONTEXT,
75 new HookContext(connectionProvider, runner, new Change(change)));
76 hook.afterEach(hookBindings);
77 }
78 stepCount++;
79 }
80 if (stepCount > 0 && hook != null) {
81 hookBindings.put(MigrationHook.HOOK_CONTEXT, new HookContext(connectionProvider, runner, null));
82 hook.after(hookBindings);
83 }
84 return this;
85 } catch (Exception e) {
86 throw new MigrationException("Error executing command. Cause: " + e, e);
87 }
88 } catch (Throwable e) {
89 while (e instanceof MigrationException && e.getCause() != null) {
90 e = e.getCause();
91 }
92 throw new MigrationException("Error executing command. Cause: " + e, e);
93 }
94 }
95
96 private List<Change> getPendingChanges(Connection con, MigrationLoader migrationsLoader,
97 DatabaseOperationOption option) {
98 List<Change> pending = new ArrayList<>();
99 List<Change> migrations = migrationsLoader.getMigrations();
100 List<Change> changelog = getChangelog(con, option);
101 for (Change change : migrations) {
102 int index = changelog.indexOf(change);
103 if (index < 0) {
104 pending.add(change);
105 }
106 }
107 pending.sort(null);
108 return pending;
109 }
110 }