Clover Coverage Report - Liquibase Core 2.0.3-SNAPSHOT
Coverage timestamp: Sat Aug 6 2011 11:33:15 EDT
../../img/srcFileCovDistChart0.png 69% of files have more coverage
157   433   89   4.36
82   343   0.57   36
36     2.47  
1    
 
  Diff       Line # 23 157 0% 89 275 0% 0.0
 
No Tests
 
1    package liquibase.diff;
2   
3    import java.util.Arrays;
4    import java.util.HashSet;
5    import java.util.Set;
6    import java.util.SortedSet;
7    import java.util.TreeSet;
8   
9    import liquibase.database.Database;
10    import liquibase.database.structure.Column;
11    import liquibase.database.structure.ForeignKey;
12    import liquibase.database.structure.Index;
13    import liquibase.database.structure.PrimaryKey;
14    import liquibase.database.structure.Sequence;
15    import liquibase.database.structure.Table;
16    import liquibase.database.structure.UniqueConstraint;
17    import liquibase.database.structure.View;
18    import liquibase.exception.DatabaseException;
19    import liquibase.snapshot.DatabaseSnapshot;
20    import liquibase.snapshot.DatabaseSnapshotGeneratorFactory;
21    import liquibase.util.StringUtils;
22   
 
23    public class Diff {
24   
25    private Database referenceDatabase;
26    private Database targetDatabase;
27   
28    private DatabaseSnapshot referenceSnapshot;
29    private DatabaseSnapshot targetSnapshot;
30   
31    private Set<DiffStatusListener> statusListeners = new HashSet<DiffStatusListener>();
32   
33    private boolean diffTables = true;
34    private boolean diffColumns = true;
35    private boolean diffViews = true;
36    private boolean diffPrimaryKeys = true;
37    private boolean diffUniqueConstraints = true;
38    private boolean diffIndexes = true;
39    private boolean diffForeignKeys = true;
40    private boolean diffSequences = true;
41    private boolean diffData = false;
42   
 
43  0 toggle public Diff(Database referenceDatabase, Database targetDatabase) {
44  0 this.referenceDatabase = referenceDatabase;
45   
46  0 this.targetDatabase = targetDatabase;
47    }
48   
 
49  0 toggle public Diff(Database originalDatabase, String schema) throws DatabaseException {
50  0 targetDatabase = null;
51   
52  0 referenceDatabase = originalDatabase;
53  0 referenceDatabase.setDefaultSchemaName(schema);
54    }
55   
 
56  0 toggle public Diff(DatabaseSnapshot referenceSnapshot, DatabaseSnapshot targetDatabaseSnapshot) {
57  0 this.referenceSnapshot = referenceSnapshot;
58   
59  0 this.targetSnapshot = targetDatabaseSnapshot;
60    }
61   
 
62  0 toggle public void addStatusListener(DiffStatusListener listener) {
63  0 statusListeners.add(listener);
64    }
65   
 
66  0 toggle public void removeStatusListener(DiffStatusListener listener) {
67  0 statusListeners.remove(listener);
68    }
69   
 
70  0 toggle public DiffResult compare() throws DatabaseException {
71  0 if (referenceSnapshot == null) {
72  0 referenceSnapshot = DatabaseSnapshotGeneratorFactory.getInstance().createSnapshot(referenceDatabase, null,
73    statusListeners);
74    }
75   
76  0 if (targetSnapshot == null) {
77  0 if (targetDatabase == null) {
78  0 targetSnapshot = new DatabaseSnapshot(referenceDatabase, null);
79    } else {
80  0 targetSnapshot = DatabaseSnapshotGeneratorFactory.getInstance().createSnapshot(targetDatabase, null,
81    statusListeners);
82    }
83    }
84   
85  0 DiffResult diffResult = new DiffResult(referenceSnapshot, targetSnapshot);
86  0 checkVersionInfo(diffResult);
87  0 if (shouldDiffTables()) {
88  0 checkTables(diffResult);
89    }
90  0 if (shouldDiffViews()) {
91  0 checkViews(diffResult);
92    }
93  0 if (shouldDiffColumns()) {
94  0 checkColumns(diffResult);
95    }
96  0 if (shouldDiffForeignKeys()) {
97  0 checkForeignKeys(diffResult);
98    }
99  0 if (shouldDiffPrimaryKeys()) {
100  0 checkPrimaryKeys(diffResult);
101    }
102  0 if (shouldDiffUniqueConstraints()) {
103  0 checkUniqueConstraints(diffResult);
104    }
105  0 if (shouldDiffIndexes()) {
106  0 checkIndexes(diffResult);
107    }
108  0 if (shouldDiffSequences()) {
109  0 checkSequences(diffResult);
110    }
111  0 diffResult.setDiffData(shouldDiffData());
112   
113    // Hack: Sometimes Indexes or Unique Constraints with multiple columns get added twice (1 for each column),
114    // so we're combining them back to a single Index or Unique Constraint here.
115  0 removeDuplicateIndexes(diffResult.getMissingIndexes());
116  0 removeDuplicateIndexes(diffResult.getUnexpectedIndexes());
117  0 removeDuplicateUniqueConstraints(diffResult.getMissingUniqueConstraints());
118  0 removeDuplicateUniqueConstraints(diffResult.getUnexpectedUniqueConstraints());
119   
120  0 return diffResult;
121    }
122   
 
123  0 toggle public void setDiffTypes(String diffTypes) {
124  0 if (StringUtils.trimToNull(diffTypes) != null) {
125  0 Set<String> types = new HashSet<String>(Arrays.asList(diffTypes.toLowerCase().split("\\s*,\\s*")));
126   
127  0 diffTables = types.contains("tables");
128  0 diffColumns = types.contains("columns");
129  0 diffViews = types.contains("views");
130  0 diffPrimaryKeys = types.contains("primaryKeys".toLowerCase());
131  0 diffUniqueConstraints = types.contains("uniqueConstraints".toLowerCase());
132  0 diffIndexes = types.contains("indexes");
133  0 diffForeignKeys = types.contains("foreignKeys".toLowerCase());
134  0 diffSequences = types.contains("sequences");
135  0 diffData = types.contains("data");
136    }
137    }
138   
 
139  0 toggle public boolean shouldDiffTables() {
140  0 return diffTables;
141    }
142   
 
143  0 toggle public void setDiffTables(boolean diffTables) {
144  0 this.diffTables = diffTables;
145    }
146   
 
147  0 toggle public boolean shouldDiffColumns() {
148  0 return diffColumns;
149    }
150   
 
151  0 toggle public void setDiffColumns(boolean diffColumns) {
152  0 this.diffColumns = diffColumns;
153    }
154   
 
155  0 toggle public boolean shouldDiffViews() {
156  0 return diffViews;
157    }
158   
 
159  0 toggle public void setDiffViews(boolean diffViews) {
160  0 this.diffViews = diffViews;
161    }
162   
 
163  0 toggle public boolean shouldDiffPrimaryKeys() {
164  0 return diffPrimaryKeys;
165    }
166   
 
167  0 toggle public void setDiffPrimaryKeys(boolean diffPrimaryKeys) {
168  0 this.diffPrimaryKeys = diffPrimaryKeys;
169    }
170   
 
171  0 toggle public boolean shouldDiffIndexes() {
172  0 return diffIndexes;
173    }
174   
 
175  0 toggle public void setDiffIndexes(boolean diffIndexes) {
176  0 this.diffIndexes = diffIndexes;
177    }
178   
 
179  0 toggle public boolean shouldDiffForeignKeys() {
180  0 return diffForeignKeys;
181    }
182   
 
183  0 toggle public void setDiffForeignKeys(boolean diffForeignKeys) {
184  0 this.diffForeignKeys = diffForeignKeys;
185    }
186   
 
187  0 toggle public boolean shouldDiffSequences() {
188  0 return diffSequences;
189    }
190   
 
191  0 toggle public void setDiffSequences(boolean diffSequences) {
192  0 this.diffSequences = diffSequences;
193    }
194   
 
195  0 toggle public boolean shouldDiffData() {
196  0 return diffData;
197    }
198   
 
199  0 toggle public void setDiffData(boolean diffData) {
200  0 this.diffData = diffData;
201    }
202   
 
203  0 toggle public boolean shouldDiffUniqueConstraints() {
204  0 return this.diffUniqueConstraints;
205    }
206   
 
207  0 toggle public void setDiffUniqueConstraints(boolean diffUniqueConstraints) {
208  0 this.diffUniqueConstraints = diffUniqueConstraints;
209    }
210   
 
211  0 toggle private void checkVersionInfo(DiffResult diffResult) throws DatabaseException {
212   
213  0 if (targetDatabase != null) {
214  0 diffResult.setProductName(new DiffComparison(referenceDatabase.getDatabaseProductName(), targetDatabase
215    .getDatabaseProductName()));
216  0 diffResult.setProductVersion(new DiffComparison(referenceDatabase.getDatabaseProductVersion(),
217    targetDatabase.getDatabaseProductVersion()));
218    }
219   
220    }
221   
 
222  0 toggle private void checkTables(DiffResult diffResult) {
223  0 for (Table baseTable : referenceSnapshot.getTables()) {
224  0 if (!targetSnapshot.getTables().contains(baseTable)) {
225  0 diffResult.addMissingTable(baseTable);
226    }
227    }
228   
229  0 for (Table targetTable : targetSnapshot.getTables()) {
230  0 if (!referenceSnapshot.getTables().contains(targetTable)) {
231  0 diffResult.addUnexpectedTable(targetTable);
232    }
233    }
234    }
235   
 
236  0 toggle private void checkViews(DiffResult diffResult) {
237  0 for (View baseView : referenceSnapshot.getViews()) {
238  0 if (!targetSnapshot.getViews().contains(baseView)) {
239  0 diffResult.addMissingView(baseView);
240    }
241    }
242   
243  0 for (View targetView : targetSnapshot.getViews()) {
244  0 if (!referenceSnapshot.getViews().contains(targetView)) {
245  0 diffResult.addUnexpectedView(targetView);
246    } else {
247  0 for (View referenceView : referenceSnapshot.getViews()) {
248  0 if (referenceView.getName().equals(targetView.getName())) {
249  0 if (!referenceView.getDefinition().equals(targetView.getDefinition())) {
250  0 diffResult.addChangedView(referenceView);
251    }
252    }
253    }
254    }
255    }
256    }
257   
 
258  0 toggle private void checkColumns(DiffResult diffResult) {
259  0 for (Column baseColumn : referenceSnapshot.getColumns()) {
260  0 if (!targetSnapshot.getColumns().contains(baseColumn)
261    && (baseColumn.getTable() == null || !diffResult.getMissingTables().contains(baseColumn.getTable()))
262    && (baseColumn.getView() == null || !diffResult.getMissingViews().contains(baseColumn.getView()))) {
263  0 diffResult.addMissingColumn(baseColumn);
264    }
265    }
266   
267  0 for (Column targetColumn : targetSnapshot.getColumns()) {
268  0 if (!referenceSnapshot.getColumns().contains(targetColumn)
269    && (targetColumn.getTable() == null || !diffResult.getUnexpectedTables().contains(
270    targetColumn.getTable()))
271    && (targetColumn.getView() == null || !diffResult.getUnexpectedViews().contains(
272    targetColumn.getView()))) {
273  0 diffResult.addUnexpectedColumn(targetColumn);
274  0 } else if (targetColumn.getTable() != null
275    && !diffResult.getUnexpectedTables().contains(targetColumn.getTable())) {
276  0 Column baseColumn = referenceSnapshot.getColumn(targetColumn.getTable().getName(),
277    targetColumn.getName());
278   
279  0 if (baseColumn == null || targetColumn.isDifferent(baseColumn)) {
280  0 diffResult.addChangedColumn(targetColumn);
281    }
282    }
283    }
284    }
285   
 
286  0 toggle private void checkForeignKeys(DiffResult diffResult) {
287  0 for (ForeignKey baseFK : referenceSnapshot.getForeignKeys()) {
288  0 if (!targetSnapshot.getForeignKeys().contains(baseFK)) {
289  0 diffResult.addMissingForeignKey(baseFK);
290    }
291    }
292   
293  0 for (ForeignKey targetFK : targetSnapshot.getForeignKeys()) {
294  0 if (!referenceSnapshot.getForeignKeys().contains(targetFK)) {
295  0 diffResult.addUnexpectedForeignKey(targetFK);
296    }
297    }
298    }
299   
 
300  0 toggle private void checkUniqueConstraints(DiffResult diffResult) {
301  0 for (UniqueConstraint baseIndex : referenceSnapshot.getUniqueConstraints()) {
302  0 if (!targetSnapshot.getUniqueConstraints().contains(baseIndex)) {
303  0 diffResult.addMissingUniqueConstraint(baseIndex);
304    }
305    }
306   
307  0 for (UniqueConstraint targetIndex : targetSnapshot.getUniqueConstraints()) {
308  0 if (!referenceSnapshot.getUniqueConstraints().contains(targetIndex)) {
309  0 diffResult.addUnexpectedUniqueConstraint(targetIndex);
310    }
311    }
312    }
313   
 
314  0 toggle private void checkIndexes(DiffResult diffResult) {
315  0 for (Index baseIndex : referenceSnapshot.getIndexes()) {
316  0 if (!targetSnapshot.getIndexes().contains(baseIndex)) {
317  0 diffResult.addMissingIndex(baseIndex);
318    }
319    }
320   
321  0 for (Index targetIndex : targetSnapshot.getIndexes()) {
322  0 if (!referenceSnapshot.getIndexes().contains(targetIndex)) {
323  0 diffResult.addUnexpectedIndex(targetIndex);
324    }
325    }
326    }
327   
 
328  0 toggle private void checkPrimaryKeys(DiffResult diffResult) {
329  0 for (PrimaryKey basePrimaryKey : referenceSnapshot.getPrimaryKeys()) {
330  0 if (!targetSnapshot.getPrimaryKeys().contains(basePrimaryKey)) {
331  0 diffResult.addMissingPrimaryKey(basePrimaryKey);
332    }
333    }
334   
335  0 for (PrimaryKey targetPrimaryKey : targetSnapshot.getPrimaryKeys()) {
336  0 if (!referenceSnapshot.getPrimaryKeys().contains(targetPrimaryKey)) {
337  0 diffResult.addUnexpectedPrimaryKey(targetPrimaryKey);
338    }
339    }
340    }
341   
 
342  0 toggle private void checkSequences(DiffResult diffResult) {
343  0 for (Sequence baseSequence : referenceSnapshot.getSequences()) {
344  0 if (!targetSnapshot.getSequences().contains(baseSequence)) {
345  0 diffResult.addMissingSequence(baseSequence);
346    }
347    }
348   
349  0 for (Sequence targetSequence : targetSnapshot.getSequences()) {
350  0 if (!referenceSnapshot.getSequences().contains(targetSequence)) {
351  0 diffResult.addUnexpectedSequence(targetSequence);
352    }
353    }
354    }
355   
356    /**
357    * Removes duplicate Indexes from the DiffResult object.
358    *
359    * @param indexes
360    * [IN/OUT] - A set of Indexes to be updated.
361    */
 
362  0 toggle private void removeDuplicateIndexes(SortedSet<Index> indexes) {
363  0 SortedSet<Index> combinedIndexes = new TreeSet<Index>();
364  0 SortedSet<Index> indexesToRemove = new TreeSet<Index>();
365   
366    // Find Indexes with the same name, copy their columns into the first one,
367    // then remove the duplicate Indexes.
368  0 for (Index idx1 : indexes) {
369  0 if (!combinedIndexes.contains(idx1)) {
370  0 for (Index idx2 : indexes.tailSet(idx1)) {
371  0 if (idx1 == idx2) {
372  0 continue;
373    }
374   
375  0 String index1Name = StringUtils.trimToEmpty(idx1.getName());
376  0 String index2Name = StringUtils.trimToEmpty(idx2.getName());
377  0 if (index1Name.equalsIgnoreCase(index2Name)
378    && idx1.getTable().getName().equalsIgnoreCase(idx2.getTable().getName())) {
379  0 for (String column : idx2.getColumns()) {
380  0 if (!idx1.getColumns().contains(column)) {
381  0 idx1.getColumns().add(column);
382    }
383    }
384   
385  0 indexesToRemove.add(idx2);
386    }
387    }
388   
389  0 combinedIndexes.add(idx1);
390    }
391    }
392   
393  0 indexes.removeAll(indexesToRemove);
394    }
395   
396    /**
397    * Removes duplicate Unique Constraints from the DiffResult object.
398    *
399    * @param uniqueConstraints
400    * [IN/OUT] - A set of Unique Constraints to be updated.
401    */
 
402  0 toggle private void removeDuplicateUniqueConstraints(SortedSet<UniqueConstraint> uniqueConstraints) {
403  0 SortedSet<UniqueConstraint> combinedConstraints = new TreeSet<UniqueConstraint>();
404  0 SortedSet<UniqueConstraint> constraintsToRemove = new TreeSet<UniqueConstraint>();
405   
406    // Find UniqueConstraints with the same name, copy their columns into the first one,
407    // then remove the duplicate UniqueConstraints.
408  0 for (UniqueConstraint uc1 : uniqueConstraints) {
409  0 if (!combinedConstraints.contains(uc1)) {
410  0 for (UniqueConstraint uc2 : uniqueConstraints.tailSet(uc1)) {
411  0 if (uc1 == uc2) {
412  0 continue;
413    }
414   
415  0 if (uc1.getName().equalsIgnoreCase(uc2.getName())
416    && uc1.getTable().getName().equalsIgnoreCase(uc2.getTable().getName())) {
417  0 for (String column : uc2.getColumns()) {
418  0 if (!uc1.getColumns().contains(column)) {
419  0 uc1.getColumns().add(column);
420    }
421    }
422   
423  0 constraintsToRemove.add(uc2);
424    }
425    }
426   
427  0 combinedConstraints.add(uc1);
428    }
429    }
430   
431  0 uniqueConstraints.removeAll(constraintsToRemove);
432    }
433    }