There appears to be a bug in:
liquibase-core/src/main/java/liquibase/diff/compare/core/ForeignKeyComparator.java
Which was changed on 8/27/2013 for the 3.0.3 release.
If you have a precondition like this:
You will encounter this error since the ForeignKeyColumns and PrimaryKeyColumns aren’t set in the change set:
Caused by: liquibase.exception.MigrationFailedException: Migration failed for change set foo.xml::xxx:
Reason:
src/main/resources/db/activFoundation.xml : liquibase.precondition.core.ForeignKeyExistsPrecondition@5f96f335 : java.lang.NullPointerException
at liquibase.changelog.ChangeSet.execute(ChangeSet.java:310)
at liquibase.changelog.visitor.UpdateVisitor.visit(UpdateVisitor.java:28)
at liquibase.changelog.ChangeLogIterator.run(ChangeLogIterator.java:58)
at liquibase.Liquibase.update(Liquibase.java:135)
at org.liquibase.maven.plugins.LiquibaseUpdate.doUpdate(LiquibaseUpdate.java:31)
at org.liquibase.maven.plugins.AbstractLiquibaseUpdateMojo.performLiquibaseTask(AbstractLiquibaseUpdateMojo.java:24)
at org.liquibase.maven.plugins.AbstractLiquibaseMojo.execute(AbstractLiquibaseMojo.java:375)
… 21 more
Caused by: liquibase.exception.PreconditionErrorException: Precondition Error
at liquibase.precondition.core.ForeignKeyExistsPrecondition.check(ForeignKeyExistsPrecondition.java:76)
at liquibase.precondition.core.NotPrecondition.check(NotPrecondition.java:30)
at liquibase.precondition.core.AndPrecondition.check(AndPrecondition.java:34)
at liquibase.precondition.core.PreconditionContainer.check(PreconditionContainer.java:199)
at liquibase.changelog.ChangeSet.execute(ChangeSet.java:274)
… 27 more
I downloaded and changed the code to this and rebuilt locally and the issue is resolved. The change is checking the value of getForeignKeyColumns() and getPrimaryKeyColumns() for null before doing the comparison.
public boolean isSameObject(DatabaseObject databaseObject1, DatabaseObject databaseObject2, Database accordingTo, DatabaseObjectComparatorChain chain) {
if (!(databaseObject1 instanceof ForeignKey && databaseObject2 instanceof ForeignKey)) {
return false;
}
ForeignKey thisForeignKey = (ForeignKey) databaseObject1;
ForeignKey otherForeignKey = (ForeignKey) databaseObject2;
if (thisForeignKey.getName() != null && otherForeignKey.getName() != null) {
if (chain.isSameObject(thisForeignKey, otherForeignKey, accordingTo)) {
return true;
}
}
if (thisForeignKey.getForeignKeyColumns() != null && thisForeignKey.getPrimaryKeyColumns() != null &&
otherForeignKey.getForeignKeyColumns() != null && otherForeignKey.getPrimaryKeyColumns() != null) {
boolean columnsTheSame;
if (accordingTo.isCaseSensitive()) {
columnsTheSame = StringUtils.trimToEmpty(thisForeignKey.getForeignKeyColumns()).equals(StringUtils.trimToEmpty(otherForeignKey.getForeignKeyColumns())) &&
StringUtils.trimToEmpty(thisForeignKey.getPrimaryKeyColumns()).equals(StringUtils.trimToEmpty(otherForeignKey.getPrimaryKeyColumns()));
} else {
columnsTheSame = thisForeignKey.getForeignKeyColumns().equalsIgnoreCase(otherForeignKey.getForeignKeyColumns()) &&
thisForeignKey.getPrimaryKeyColumns().equalsIgnoreCase(otherForeignKey.getPrimaryKeyColumns());
return columnsTheSame &&
DatabaseObjectComparatorFactory.getInstance().isSameObject(thisForeignKey.getForeignKeyTable(), otherForeignKey.getForeignKeyTable(), accordingTo) &&
DatabaseObjectComparatorFactory.getInstance().isSameObject(thisForeignKey.getPrimaryKeyTable(), otherForeignKey.getPrimaryKeyTable(), accordingTo);
}
}
//false by default
return false;
}
I’m not sure this is the optimal solution, but certainly works.
Thanks in advance for help with this.
Andy