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