From f96313bdc609979776daf2f4667a3f61e8c88cfb Mon Sep 17 00:00:00 2001 From: filipe Date: Thu, 11 Apr 2024 16:24:19 -0300 Subject: [PATCH 01/10] fix: backing index should not be unique. Introduced in commit 0bbd33c17f3eef5ddbd188a625e203ae3076831d from 2013. --- .../hibernate/snapshot/UniqueConstraintSnapshotGenerator.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java b/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java index f1cd4b44..f1a56d9d 100644 --- a/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java +++ b/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java @@ -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 { @@ -107,7 +106,7 @@ protected Index getBackingIndex(UniqueConstraint uniqueConstraint, org.hibernate Index index = new Index(); index.setRelation(uniqueConstraint.getRelation()); index.setColumns(uniqueConstraint.getColumns()); - index.setUnique(true); + index.setUnique(false); index.setName(String.format("%s_%s_IX",hibernateTable.getName(), StringUtil.randomIdentifer(4))); return index; From be55d37ac8b06751f36548fba32c3783396c9b22 Mon Sep 17 00:00:00 2001 From: filipe Date: Thu, 11 Apr 2024 16:43:00 -0300 Subject: [PATCH 02/10] fix: imlpement the fix when snapshoting so it's consistent with what we are creating. --- .../snapshot/UniqueConstraintSnapshotGenerator.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java b/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java index f1a56d9d..4a466347 100644 --- a/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java +++ b/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java @@ -24,6 +24,10 @@ public UniqueConstraintSnapshotGenerator() { @Override protected DatabaseObject snapshotObject(DatabaseObject example, DatabaseSnapshot snapshot) throws DatabaseException, InvalidExampleException { + UniqueConstraint exampleUniqueConstraint = (UniqueConstraint) example; + if (exampleUniqueConstraint.getBackingIndex() != null) { + exampleUniqueConstraint.getBackingIndex().setUnique(true); + } return example; } @@ -106,7 +110,7 @@ protected Index getBackingIndex(UniqueConstraint uniqueConstraint, org.hibernate Index index = new Index(); index.setRelation(uniqueConstraint.getRelation()); index.setColumns(uniqueConstraint.getColumns()); - index.setUnique(false); + index.setUnique(true); index.setName(String.format("%s_%s_IX",hibernateTable.getName(), StringUtil.randomIdentifer(4))); return index; From 8e4b5b83eb093502efcf246919bc2bbfa7653885 Mon Sep 17 00:00:00 2001 From: filipe Date: Thu, 11 Apr 2024 23:59:28 -0300 Subject: [PATCH 03/10] chore: rollback changes, fix is not here. --- .../snapshot/UniqueConstraintSnapshotGenerator.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java b/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java index 4a466347..ee78be5a 100644 --- a/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java +++ b/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java @@ -14,7 +14,7 @@ import java.math.BigInteger; import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; +import java.security.NoSuchAlgorithmException;= public class UniqueConstraintSnapshotGenerator extends HibernateSnapshotGenerator { @@ -24,10 +24,6 @@ public UniqueConstraintSnapshotGenerator() { @Override protected DatabaseObject snapshotObject(DatabaseObject example, DatabaseSnapshot snapshot) throws DatabaseException, InvalidExampleException { - UniqueConstraint exampleUniqueConstraint = (UniqueConstraint) example; - if (exampleUniqueConstraint.getBackingIndex() != null) { - exampleUniqueConstraint.getBackingIndex().setUnique(true); - } return example; } From d39b28668efeb5982d8cf947f9478b2f34684b4a Mon Sep 17 00:00:00 2001 From: filipe Date: Fri, 12 Apr 2024 00:00:06 -0300 Subject: [PATCH 04/10] fix: how a unique constraint is not unique!? --- ...hangedUniqueConstraintChangeGenerator.java | 32 +++++++++++++++++++ ...base.diff.output.changelog.ChangeGenerator | 1 + 2 files changed, 33 insertions(+) create mode 100644 src/main/java/liquibase/ext/hibernate/diff/ChangedUniqueConstraintChangeGenerator.java diff --git a/src/main/java/liquibase/ext/hibernate/diff/ChangedUniqueConstraintChangeGenerator.java b/src/main/java/liquibase/ext/hibernate/diff/ChangedUniqueConstraintChangeGenerator.java new file mode 100644 index 00000000..3a0f9aac --- /dev/null +++ b/src/main/java/liquibase/ext/hibernate/diff/ChangedUniqueConstraintChangeGenerator.java @@ -0,0 +1,32 @@ +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; + +public class ChangedUniqueConstraintChangeGenerator extends liquibase.diff.output.changelog.core.ChangedUniqueConstraintChangeGenerator { + + @Override + public int getPriority(Class 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); + } +} diff --git a/src/main/resources/META-INF/services/liquibase.diff.output.changelog.ChangeGenerator b/src/main/resources/META-INF/services/liquibase.diff.output.changelog.ChangeGenerator index 28466d96..c6497670 100644 --- a/src/main/resources/META-INF/services/liquibase.diff.output.changelog.ChangeGenerator +++ b/src/main/resources/META-INF/services/liquibase.diff.output.changelog.ChangeGenerator @@ -1,5 +1,6 @@ liquibase.ext.hibernate.diff.ChangedColumnChangeGenerator liquibase.ext.hibernate.diff.ChangedForeignKeyChangeGenerator liquibase.ext.hibernate.diff.ChangedSequenceChangeGenerator +liquibase.ext.hibernate.diff.ChangedUniqueConstraintChangeGenerator liquibase.ext.hibernate.diff.MissingSequenceChangeGenerator liquibase.ext.hibernate.diff.UnexpectedIndexChangeGenerator From 8851f90ca281a5ee5bc1d364a062c65470518bb8 Mon Sep 17 00:00:00 2001 From: filipe Date: Fri, 12 Apr 2024 00:11:15 -0300 Subject: [PATCH 05/10] fix: typo --- .../snapshot/UniqueConstraintSnapshotGenerator.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java b/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java index ee78be5a..6c665565 100644 --- a/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java +++ b/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java @@ -14,7 +14,7 @@ import java.math.BigInteger; import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException;= +import java.security.NoSuchAlgorithmException; public class UniqueConstraintSnapshotGenerator extends HibernateSnapshotGenerator { @@ -53,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()) { @@ -67,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); @@ -102,7 +102,7 @@ private String hashedName(String s) { } } - protected Index getBackingIndex(UniqueConstraint uniqueConstraint, org.hibernate.mapping.Table hibernateTable, DatabaseSnapshot snapshot) { + protected Index getBackingIndex(UniqueConstraint uniqueConstraint, org.hibernate.mapping.Table hibernateTable) { Index index = new Index(); index.setRelation(uniqueConstraint.getRelation()); index.setColumns(uniqueConstraint.getColumns()); From b493b5dbc4c6449c486ab734399ffceda9dfe699 Mon Sep 17 00:00:00 2001 From: filipe Date: Fri, 12 Apr 2024 00:13:35 -0300 Subject: [PATCH 06/10] fix: build --- .../hibernate/snapshot/UniqueConstraintSnapshotGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java b/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java index 6c665565..5678a424 100644 --- a/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java +++ b/src/main/java/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGenerator.java @@ -102,7 +102,7 @@ private String hashedName(String s) { } } - protected Index getBackingIndex(UniqueConstraint uniqueConstraint, org.hibernate.mapping.Table hibernateTable) { + protected Index getBackingIndex(UniqueConstraint uniqueConstraint, org.hibernate.mapping.Table hibernateTable, DatabaseSnapshot snapshot) { Index index = new Index(); index.setRelation(uniqueConstraint.getRelation()); index.setColumns(uniqueConstraint.getColumns()); From 822ee04922112e613927ad8b9fc21b02b412c79d Mon Sep 17 00:00:00 2001 From: filipe Date: Fri, 19 Apr 2024 12:13:23 -0300 Subject: [PATCH 07/10] fix: primary key backing index issues. --- .../ChangedPrimaryKeyChangeGenerator.java | 37 +++++++++++++++++++ ...hangedUniqueConstraintChangeGenerator.java | 5 +++ ...base.diff.output.changelog.ChangeGenerator | 1 + 3 files changed, 43 insertions(+) create mode 100644 src/main/java/liquibase/ext/hibernate/diff/ChangedPrimaryKeyChangeGenerator.java diff --git a/src/main/java/liquibase/ext/hibernate/diff/ChangedPrimaryKeyChangeGenerator.java b/src/main/java/liquibase/ext/hibernate/diff/ChangedPrimaryKeyChangeGenerator.java new file mode 100644 index 00000000..22787266 --- /dev/null +++ b/src/main/java/liquibase/ext/hibernate/diff/ChangedPrimaryKeyChangeGenerator.java @@ -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 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); + } +} diff --git a/src/main/java/liquibase/ext/hibernate/diff/ChangedUniqueConstraintChangeGenerator.java b/src/main/java/liquibase/ext/hibernate/diff/ChangedUniqueConstraintChangeGenerator.java index 3a0f9aac..204c12b2 100644 --- a/src/main/java/liquibase/ext/hibernate/diff/ChangedUniqueConstraintChangeGenerator.java +++ b/src/main/java/liquibase/ext/hibernate/diff/ChangedUniqueConstraintChangeGenerator.java @@ -9,6 +9,11 @@ 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 diff --git a/src/main/resources/META-INF/services/liquibase.diff.output.changelog.ChangeGenerator b/src/main/resources/META-INF/services/liquibase.diff.output.changelog.ChangeGenerator index c6497670..7025ee17 100644 --- a/src/main/resources/META-INF/services/liquibase.diff.output.changelog.ChangeGenerator +++ b/src/main/resources/META-INF/services/liquibase.diff.output.changelog.ChangeGenerator @@ -1,5 +1,6 @@ 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 From 612885e86d43e18c2d42c280357f2dafae8505a5 Mon Sep 17 00:00:00 2001 From: filipe Date: Fri, 19 Apr 2024 14:19:35 -0300 Subject: [PATCH 08/10] fix: advanced validation for sequence fields managed by liquibase --- .../diff/ChangedSequenceChangeGenerator.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/main/java/liquibase/ext/hibernate/diff/ChangedSequenceChangeGenerator.java b/src/main/java/liquibase/ext/hibernate/diff/ChangedSequenceChangeGenerator.java index 6878f91b..9d3c9120 100644 --- a/src/main/java/liquibase/ext/hibernate/diff/ChangedSequenceChangeGenerator.java +++ b/src/main/java/liquibase/ext/hibernate/diff/ChangedSequenceChangeGenerator.java @@ -53,7 +53,31 @@ 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); } + private void advancedIgnoredDifferenceFields(ObjectDifferences differences, Database referenceDatabase, Database comparisonDatabase) { + Set 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 + boolean isStartOrIncrementField = field.equals("startValue") || field.equals("incrementBy"); + boolean isOneAndNull = "1".equals(refValue) && comparedValue == null || refValue == null && "1".equals(comparedValue); + + if ((isNameField && isCaseInsensitive && refValue != null && refValue.equalsIgnoreCase(comparedValue)) || + (isStartOrIncrementField && isOneAndNull)) { + ignoredDifferenceFields.add(field); + } + } + ignoredDifferenceFields.forEach(differences::removeDifference); + } + } From 33b8b693b48377ca06e6c707abebbe2fb547f24b Mon Sep 17 00:00:00 2001 From: filipe Date: Tue, 23 Apr 2024 08:23:02 -0300 Subject: [PATCH 09/10] chore(docs): javadocs --- .../ext/hibernate/diff/ChangedSequenceChangeGenerator.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/liquibase/ext/hibernate/diff/ChangedSequenceChangeGenerator.java b/src/main/java/liquibase/ext/hibernate/diff/ChangedSequenceChangeGenerator.java index 9d3c9120..b8373bc6 100644 --- a/src/main/java/liquibase/ext/hibernate/diff/ChangedSequenceChangeGenerator.java +++ b/src/main/java/liquibase/ext/hibernate/diff/ChangedSequenceChangeGenerator.java @@ -57,6 +57,11 @@ public Change[] fixChanged(DatabaseObject changedObject, ObjectDifferences diffe 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 ignoredDifferenceFields = new HashSet<>(); for (Difference difference : differences.getDifferences()) { @@ -64,7 +69,7 @@ private void advancedIgnoredDifferenceFields(ObjectDifferences differences, Data 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 + // 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(); From eb109c37fd83accd0a79a9d1809410692b1920f8 Mon Sep 17 00:00:00 2001 From: filipe Date: Tue, 23 Apr 2024 14:06:01 -0300 Subject: [PATCH 10/10] fix: allow allocation size of 50 to be the same as null --- .../ext/hibernate/diff/ChangedSequenceChangeGenerator.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/liquibase/ext/hibernate/diff/ChangedSequenceChangeGenerator.java b/src/main/java/liquibase/ext/hibernate/diff/ChangedSequenceChangeGenerator.java index b8373bc6..9b55a8f3 100644 --- a/src/main/java/liquibase/ext/hibernate/diff/ChangedSequenceChangeGenerator.java +++ b/src/main/java/liquibase/ext/hibernate/diff/ChangedSequenceChangeGenerator.java @@ -74,11 +74,14 @@ private void advancedIgnoredDifferenceFields(ObjectDifferences differences, Data 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 isOneAndNull = "1".equals(refValue) && comparedValue == null || refValue == null && "1".equals(comparedValue); + 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 && isOneAndNull)) { + (isStartOrIncrementField && isOneOrFiftyAndNull)) { ignoredDifferenceFields.add(field); } }