001// Copyright 2014 Leo Przybylski. All rights reserved. 002// 003// Redistribution and use in source and binary forms, with or without modification, are 004// permitted provided that the following conditions are met: 005// 006// 1. Redistributions of source code must retain the above copyright notice, this list of 007// conditions and the following disclaimer. 008// 009// 2. Redistributions in binary form must reproduce the above copyright notice, this list 010// of conditions and the following disclaimer in the documentation and/or other materials 011// provided with the distribution. 012// 013// THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY EXPRESS OR IMPLIED 014// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 015// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR 016// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 017// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 018// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 019// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 020// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 021// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 022// 023// The views and conclusions contained in the software and documentation are those of the 024// authors and should not be interpreted as representing official policies, either expressed 025// or implied, of Leo Przybylski. 026package liquibase.ext.kualigan.diff; 027 028 029import liquibase.database.Database; 030import liquibase.diff.DiffResult; 031import liquibase.diff.ObjectDifferences; 032import liquibase.diff.StringDiff; 033import liquibase.diff.compare.CompareControl; 034import liquibase.diff.compare.DatabaseObjectComparatorFactory; 035import liquibase.exception.DatabaseException; 036import liquibase.exception.UnexpectedLiquibaseException; 037import liquibase.servicelocator.PrioritizedService; 038import liquibase.snapshot.DatabaseSnapshot; 039import liquibase.snapshot.EmptyDatabaseSnapshot; 040import liquibase.snapshot.InvalidExampleException; 041import liquibase.structure.DatabaseObject; 042import liquibase.structure.core.Column; 043import liquibase.structure.core.Index; 044import liquibase.structure.core.Table; 045import liquibase.structure.core.UniqueConstraint; 046import liquibase.structure.core.View; 047import liquibase.util.StringUtils; 048 049import java.util.Arrays; 050import java.util.HashSet; 051import java.util.Set; 052import java.util.SortedSet; 053 054import static liquibase.ext.Constants.EXTENSION_PRIORITY; 055 056/** 057 * Extension of {@link liquibase.diff.DiffGenerator}. Add support for JDBC types and schema-agnostic handling 058 * 059 * @author Leo Przybylski 060 */ 061public class DiffGenerator implements liquibase.diff.DiffGenerator { 062 @Override 063 public int getPriority() { 064 return EXTENSION_PRIORITY; 065 } 066 067 @Override 068 public boolean supports(final Database referenceDatabase, final Database comparisonDatabase) { 069 return true; 070 } 071 072 @Override 073 public DiffResult compare(final DatabaseSnapshot referenceSnapshot, 074 DatabaseSnapshot comparisonSnapshot, 075 final CompareControl compareControl) throws DatabaseException { 076 077 078 if (comparisonSnapshot == null) { 079 try { 080 comparisonSnapshot = new EmptyDatabaseSnapshot(referenceSnapshot.getDatabase()); //, compareControl.toSnapshotControl(CompareControl.DatabaseRole.REFERENCE)); 081 } catch (InvalidExampleException e) { 082 throw new UnexpectedLiquibaseException(e); 083 } 084 } 085 086 final DiffResult diffResult = new DiffResult(referenceSnapshot, comparisonSnapshot, compareControl); 087 checkVersionInfo(referenceSnapshot, comparisonSnapshot, diffResult); 088 089 final Set<Class<? extends DatabaseObject>> typesToCompare = compareControl.getComparedTypes(); 090 typesToCompare.retainAll(referenceSnapshot.getSnapshotControl().getTypesToInclude()); 091 typesToCompare.retainAll(comparisonSnapshot.getSnapshotControl().getTypesToInclude()); 092 093 for (final Class<? extends DatabaseObject> typeToCompare : typesToCompare) { 094 compareObjectType(typeToCompare, referenceSnapshot, comparisonSnapshot, diffResult); 095 } 096 097 // // Hack: Sometimes Indexes or Unique Constraints with multiple columns get added twice (1 for each column), 098 // // so we're combining them back to a single Index or Unique Constraint here. 099 // removeDuplicateIndexes( diffResult.getMissingIndexes() ); 100 // removeDuplicateIndexes( diffResult.getUnexpectedIndexes() ); 101 // removeDuplicateUniqueConstraints( diffResult.getMissingUniqueConstraints() ); 102 // removeDuplicateUniqueConstraints( diffResult.getUnexpectedUniqueConstraints() ); 103 104 return diffResult; 105 } 106 107 protected <T extends DatabaseObject> void compareObjectType(Class<T> type, DatabaseSnapshot referenceSnapshot, DatabaseSnapshot comparisonSnapshot, DiffResult diffResult) { 108 109 CompareControl.SchemaComparison[] schemaComparisons = diffResult.getCompareControl().getSchemaComparisons(); 110 if (schemaComparisons != null) { 111 for (CompareControl.SchemaComparison schemaComparison : schemaComparisons) { 112 for (T referenceObject : referenceSnapshot.get(type)) { 113 // if (referenceObject instanceof Table && referenceSnapshot.getDatabase().isLiquibaseTable(referenceSchema, referenceObject.getName())) { 114 // continue; 115 // } 116 T comparisonObject = comparisonSnapshot.get(referenceObject); 117 if (comparisonObject == null) { 118 diffResult.addMissingObject(referenceObject); 119 } else { 120 ObjectDifferences differences = DatabaseObjectComparatorFactory.getInstance().findDifferences(referenceObject, comparisonObject, comparisonSnapshot.getDatabase(), diffResult.getCompareControl()); 121 if (differences.hasDifferences()) { 122 diffResult.addChangedObject(referenceObject, differences); 123 } 124 } 125 } 126 // 127 for (T comparisonObject : comparisonSnapshot.get(type)) { 128 // if (targetObject instanceof Table && comparisonSnapshot.getDatabase().isLiquibaseTable(comparisonSchema, targetObject.getName())) { 129 // continue; 130 // } 131 if (referenceSnapshot.get(comparisonObject) == null) { 132 diffResult.addUnexpectedObject(comparisonObject); 133 } 134 // } 135 } 136 } 137 138 //todo: add logic for when container is missing or unexpected also 139 } 140 } 141 142 protected void checkVersionInfo(final DatabaseSnapshot referenceSnapshot, 143 final DatabaseSnapshot comparisonSnapshot, 144 final DiffResult diffResult) throws DatabaseException { 145 146 if (comparisonSnapshot != null && comparisonSnapshot.getDatabase() != null) { 147 diffResult.setProductNameDiff(new StringDiff(referenceSnapshot.getDatabase().getDatabaseProductName(), comparisonSnapshot.getDatabase().getDatabaseProductName())); 148 diffResult.setProductVersionDiff(new StringDiff(referenceSnapshot.getDatabase().getDatabaseProductVersion(), comparisonSnapshot.getDatabase().getDatabaseProductVersion())); 149 } 150 151 } 152} 153