Possible bug in ForeignKeyComparator.isSameObject() introduced by 3.0.3 release.

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::zzz:
     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

Thanks for the report and the code change. I incorporated your change with a small fix and that seems to solve the problem for me. I’ll make sure there are no other issues with 3.0.3 and release 3.0.4 with the fix later this week.


Nathan

got a nullpointer here too,


thanks for the quick response. liquibase rocks!