Skip to content

Commit

Permalink
fix diff changelog generating undesired changes (#679)
Browse files Browse the repository at this point in the history
* fix: how a unique constraint is not unique!?

* fix: primary key backing index issues.

* fix: advanced validation for sequence fields managed by liquibase

* fix: allow allocation size of 50 to be the same as null
  • Loading branch information
filipelautert authored Apr 24, 2024
1 parent 2ef942f commit 36ad460
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package liquibase.ext.hibernate.diff;

import liquibase.change.Change;
import liquibase.database.Database;
import liquibase.diff.ObjectDifferences;
import liquibase.diff.output.DiffOutputControl;
import liquibase.diff.output.changelog.ChangeGeneratorChain;
import liquibase.ext.hibernate.database.HibernateDatabase;
import liquibase.structure.DatabaseObject;
import liquibase.structure.core.PrimaryKey;

/**
* Hibernate doesn't know about all the variations that occur with primary keys, especially backing index stuff.
* To prevent changing customized primary keys, we suppress this kind of changes from hibernate side.
*/
public class ChangedPrimaryKeyChangeGenerator extends liquibase.diff.output.changelog.core.ChangedPrimaryKeyChangeGenerator {

@Override
public int getPriority(Class<? extends DatabaseObject> objectType, Database database) {
if (PrimaryKey.class.isAssignableFrom(objectType)) {
return PRIORITY_ADDITIONAL;
}
return PRIORITY_NONE;
}

@Override
public Change[] fixChanged(DatabaseObject changedObject, ObjectDifferences differences, DiffOutputControl control, Database referenceDatabase, Database comparisonDatabase, ChangeGeneratorChain chain) {
if (referenceDatabase instanceof HibernateDatabase || comparisonDatabase instanceof HibernateDatabase) {
differences.removeDifference("unique");
if (!differences.hasDifferences()) {
return null;
}
}

return super.fixChanged(changedObject, differences, control, referenceDatabase, comparisonDatabase, chain);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,39 @@ public Change[] fixChanged(DatabaseObject changedObject, ObjectDifferences diffe
.filter(differenceField -> !HIBERNATE_SEQUENCE_FIELDS.contains(differenceField))
.collect(Collectors.toCollection(LinkedHashSet::new));
ignoredDifferenceFields.forEach(differences::removeDifference);
this.advancedIgnoredDifferenceFields(differences, referenceDatabase, comparisonDatabase);
return super.fixChanged(changedObject, differences, control, referenceDatabase, comparisonDatabase, chain);
}


/**
* In some cases a value that was 1 can be null in the database, or the name field can be different only by case.
* This method removes these differences from the list of differences so we don't generate a change for them.
*/
private void advancedIgnoredDifferenceFields(ObjectDifferences differences, Database referenceDatabase, Database comparisonDatabase) {
Set<String> ignoredDifferenceFields = new HashSet<>();
for (Difference difference : differences.getDifferences()) {
String field = difference.getField();
String refValue = difference.getReferenceValue() != null ? difference.getReferenceValue().toString() : null;
String comparedValue = difference.getComparedValue() != null ? difference.getComparedValue().toString() : null;

// if the name field case is different and the databases are case-insensitive, we can ignore the difference
boolean isNameField = field.equals("name");
boolean isCaseInsensitive = !referenceDatabase.isCaseSensitive() || !comparisonDatabase.isCaseSensitive();

// if the startValue or incrementBy fields are 1 and the other is null, we can ignore the difference
// Or 50, as it is the default value for hibernate for allocationSize:
// https://github.com/hibernate/hibernate-orm/blob/bda95dfbe75c68f5c1b77a2f21c403cbe08548a2/hibernate-core/src/main/java/org/hibernate/boot/model/IdentifierGeneratorDefinition.java#L252
boolean isStartOrIncrementField = field.equals("startValue") || field.equals("incrementBy");
boolean isOneOrFiftyAndNull = "1".equals(refValue) && comparedValue == null || refValue == null && "1".equals(comparedValue) ||
"50".equals(refValue) && comparedValue == null || refValue == null && "50".equals(comparedValue);

if ((isNameField && isCaseInsensitive && refValue != null && refValue.equalsIgnoreCase(comparedValue)) ||
(isStartOrIncrementField && isOneOrFiftyAndNull)) {
ignoredDifferenceFields.add(field);
}
}
ignoredDifferenceFields.forEach(differences::removeDifference);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package liquibase.ext.hibernate.diff;

import liquibase.change.Change;
import liquibase.database.Database;
import liquibase.diff.ObjectDifferences;
import liquibase.diff.output.DiffOutputControl;
import liquibase.diff.output.changelog.ChangeGeneratorChain;
import liquibase.ext.hibernate.database.HibernateDatabase;
import liquibase.structure.DatabaseObject;
import liquibase.structure.core.UniqueConstraint;

/**
* Unique attribute for unique constraints backing index can have different values dependending on the database implementation,
* so we suppress all unique constraint changes based on unique constraints.
*/
public class ChangedUniqueConstraintChangeGenerator extends liquibase.diff.output.changelog.core.ChangedUniqueConstraintChangeGenerator {

@Override
public int getPriority(Class<? extends DatabaseObject> objectType, Database database) {
if (UniqueConstraint.class.isAssignableFrom(objectType)) {
return PRIORITY_ADDITIONAL;
}
return PRIORITY_NONE;
}

@Override
public Change[] fixChanged(DatabaseObject changedObject, ObjectDifferences differences, DiffOutputControl control, Database referenceDatabase, Database comparisonDatabase, ChangeGeneratorChain chain) {
if (referenceDatabase instanceof HibernateDatabase || comparisonDatabase instanceof HibernateDatabase) {
differences.removeDifference("unique");
if (!differences.hasDifferences()) {
return null;
}
}
return super.fixChanged(changedObject, differences, control, referenceDatabase, comparisonDatabase, chain);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;

public class UniqueConstraintSnapshotGenerator extends HibernateSnapshotGenerator {

Expand Down Expand Up @@ -54,7 +53,7 @@ protected void addTo(DatabaseObject foundObject, DatabaseSnapshot snapshot) thro
Index index = getBackingIndex(uniqueConstraint, hibernateTable, snapshot);
uniqueConstraint.setBackingIndex(index);

Scope.getCurrentScope().getLog(getClass()).info("Found unique constraint " + uniqueConstraint.toString());
Scope.getCurrentScope().getLog(getClass()).info("Found unique constraint " + uniqueConstraint);
table.getUniqueConstraints().add(uniqueConstraint);
}
for (var column : hibernateTable.getColumns()) {
Expand All @@ -68,7 +67,7 @@ protected void addTo(DatabaseObject foundObject, DatabaseSnapshot snapshot) thro
}
uniqueConstraint.addColumn(0, new Column(column.getName()).setRelation(table));
uniqueConstraint.setName(name);
Scope.getCurrentScope().getLog(getClass()).info("Found unique constraint " + uniqueConstraint.toString());
Scope.getCurrentScope().getLog(getClass()).info("Found unique constraint " + uniqueConstraint);
table.getUniqueConstraints().add(uniqueConstraint);

Index index = getBackingIndex(uniqueConstraint, hibernateTable, snapshot);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
liquibase.ext.hibernate.diff.ChangedColumnChangeGenerator
liquibase.ext.hibernate.diff.ChangedForeignKeyChangeGenerator
liquibase.ext.hibernate.diff.ChangedPrimaryKeyChangeGenerator
liquibase.ext.hibernate.diff.ChangedSequenceChangeGenerator
liquibase.ext.hibernate.diff.ChangedUniqueConstraintChangeGenerator
liquibase.ext.hibernate.diff.MissingSequenceChangeGenerator
liquibase.ext.hibernate.diff.UnexpectedIndexChangeGenerator

0 comments on commit 36ad460

Please sign in to comment.