View Javadoc
1   /*
2    *    Copyright 2010-2026 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  
18  import java.io.PrintStream;
19  import java.sql.Connection;
20  import java.sql.SQLException;
21  import java.util.ArrayList;
22  import java.util.HashSet;
23  import java.util.List;
24  import java.util.Set;
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.options.DatabaseOperationOption;
31  import org.apache.ibatis.migration.utils.Util;
32  
33  public final class StatusOperation extends DatabaseOperation {
34  
35    private int applied;
36    private int pending;
37    private int missing;
38  
39    private List<Change> changes;
40  
41    public StatusOperation operate(ConnectionProvider connectionProvider, MigrationLoader migrationsLoader,
42        DatabaseOperationOption option, PrintStream printStream) {
43      if (option == null) {
44        option = new DatabaseOperationOption();
45      }
46      println(printStream, "ID             Applied At          Description");
47      println(printStream, Util.horizontalLine("", 80));
48      changes = new ArrayList<>();
49      List<Change> migrations = migrationsLoader.getMigrations();
50      String skippedOrMissing = null;
51      try (Connection con = connectionProvider.getConnection()) {
52        if (changelogExists(con, option)) {
53          List<Change> changelog = getChangelog(con, option);
54          skippedOrMissing = checkSkippedOrMissing(changelog, migrations);
55  
56          Set<Change> changelogAndMigrations = new HashSet<>(changelog);
57          changelogAndMigrations.addAll(migrations);
58  
59          for (Change change : changelogAndMigrations) {
60            if (!migrations.contains(change)) {
61              change = new MissingScript(change);
62              missing++;
63            } else if (change.getAppliedTimestamp() != null) {
64              applied++;
65            } else {
66              pending++;
67            }
68            changes.add(change);
69          }
70        } else {
71          changes.addAll(migrations);
72          pending = migrations.size();
73        }
74      } catch (SQLException e) {
75        throw new MigrationException("Error getting connection. Cause: " + e, e);
76      }
77  
78      changes.sort(null);
79      for (Change change : changes) {
80        println(printStream, change.toString());
81      }
82      println(printStream);
83  
84      if (skippedOrMissing != null && !skippedOrMissing.isEmpty()) {
85        println(printStream, skippedOrMissing);
86      }
87  
88      return this;
89    }
90  
91    public int getAppliedCount() {
92      return applied;
93    }
94  
95    public int getPendingCount() {
96      return pending;
97    }
98  
99    public int getMissingCount() {
100     return missing;
101   }
102 
103   public List<Change> getCurrentStatus() {
104     return changes;
105   }
106 
107   class MissingScript extends Change {
108     public MissingScript(Change change) {
109       super(change);
110     }
111 
112     @Override
113     public String toString() {
114       return super.toString() + " <=== MISSING!";
115     }
116   }
117 }