PendingOperation.java

  1. /*
  2.  *    Copyright 2010-2023 the original author or authors.
  3.  *
  4.  *    Licensed under the Apache License, Version 2.0 (the "License");
  5.  *    you may not use this file except in compliance with the License.
  6.  *    You may obtain a copy of the License at
  7.  *
  8.  *       https://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  *    Unless required by applicable law or agreed to in writing, software
  11.  *    distributed under the License is distributed on an "AS IS" BASIS,
  12.  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  *    See the License for the specific language governing permissions and
  14.  *    limitations under the License.
  15.  */
  16. package org.apache.ibatis.migration.operations;

  17. import java.io.PrintStream;
  18. import java.io.Reader;
  19. import java.sql.Connection;
  20. import java.util.ArrayList;
  21. import java.util.Collections;
  22. import java.util.HashMap;
  23. import java.util.List;
  24. import java.util.Map;

  25. import org.apache.ibatis.migration.Change;
  26. import org.apache.ibatis.migration.ConnectionProvider;
  27. import org.apache.ibatis.migration.MigrationException;
  28. import org.apache.ibatis.migration.MigrationLoader;
  29. import org.apache.ibatis.migration.hook.HookContext;
  30. import org.apache.ibatis.migration.hook.MigrationHook;
  31. import org.apache.ibatis.migration.options.DatabaseOperationOption;
  32. import org.apache.ibatis.migration.utils.Util;

  33. public final class PendingOperation extends DatabaseOperation {

  34.   public PendingOperation operate(ConnectionProvider connectionProvider, MigrationLoader migrationsLoader,
  35.       DatabaseOperationOption option, PrintStream printStream) {
  36.     return operate(connectionProvider, migrationsLoader, option, printStream, null);
  37.   }

  38.   public PendingOperation operate(ConnectionProvider connectionProvider, MigrationLoader migrationsLoader,
  39.       DatabaseOperationOption option, PrintStream printStream, MigrationHook hook) {
  40.     try (Connection con = connectionProvider.getConnection()) {
  41.       if (option == null) {
  42.         option = new DatabaseOperationOption();
  43.       }
  44.       if (!changelogExists(con, option)) {
  45.         throw new MigrationException("Change log doesn't exist, no migrations applied.  Try running 'up' instead.");
  46.       }
  47.       List<Change> pending = getPendingChanges(con, migrationsLoader, option);
  48.       int stepCount = 0;
  49.       Map<String, Object> hookBindings = new HashMap<>();
  50.       println(printStream, "WARNING: Running pending migrations out of order can create unexpected results.");
  51.       try {
  52.         ScriptRunner runner = getScriptRunner(con, option, printStream);
  53.         for (Change change : pending) {
  54.           if (stepCount == 0 && hook != null) {
  55.             hookBindings.put(MigrationHook.HOOK_CONTEXT, new HookContext(connectionProvider, runner, null));
  56.             hook.before(hookBindings);
  57.           }
  58.           if (hook != null) {
  59.             hookBindings.put(MigrationHook.HOOK_CONTEXT,
  60.                 new HookContext(connectionProvider, runner, new Change(change)));
  61.             hook.beforeEach(hookBindings);
  62.           }
  63.           println(printStream, Util.horizontalLine("Applying: " + change.getFilename(), 80));
  64.           try (Reader scriptReader = migrationsLoader.getScriptReader(change, false)) {
  65.             runner.runScript(scriptReader);
  66.           }
  67.           insertChangelog(change, con, option);
  68.           println(printStream);
  69.           if (hook != null) {
  70.             hookBindings.put(MigrationHook.HOOK_CONTEXT,
  71.                 new HookContext(connectionProvider, runner, new Change(change)));
  72.             hook.afterEach(hookBindings);
  73.           }
  74.           stepCount++;
  75.         }
  76.         if (stepCount > 0 && hook != null) {
  77.           hookBindings.put(MigrationHook.HOOK_CONTEXT, new HookContext(connectionProvider, runner, null));
  78.           hook.after(hookBindings);
  79.         }
  80.         return this;
  81.       } catch (Exception e) {
  82.         throw new MigrationException("Error executing command.  Cause: " + e, e);
  83.       }
  84.     } catch (Throwable e) {
  85.       while (e instanceof MigrationException && e.getCause() != null) {
  86.         e = e.getCause();
  87.       }
  88.       throw new MigrationException("Error executing command.  Cause: " + e, e);
  89.     }
  90.   }

  91.   private List<Change> getPendingChanges(Connection con, MigrationLoader migrationsLoader,
  92.       DatabaseOperationOption option) {
  93.     List<Change> pending = new ArrayList<>();
  94.     List<Change> migrations = migrationsLoader.getMigrations();
  95.     List<Change> changelog = getChangelog(con, option);
  96.     for (Change change : migrations) {
  97.       int index = changelog.indexOf(change);
  98.       if (index < 0) {
  99.         pending.add(change);
  100.       }
  101.     }
  102.     Collections.sort(pending);
  103.     return pending;
  104.   }
  105. }