Search code examples
database-migrationliquibaseliquibase-sql

Understanding the behaviour of logicalFilePath in liquibase


One of my changesets had a logicalFilePath which was incorrect(two changesets accidentally had the same logicalFilePath) and upon editing the logicalFilePath in an existing changeset, liquibase update failed with an error of duplicate column, which would mean that liquibase thought the changeset to be not executed and re-ran it.

Does liquibase identify if a changeset has already been executed based on the 'EXECUTED' flag or the combination of 'id','author' and 'logicalFilePath'?

Also, how do i rectify the mistake in this case where an existing changeset has an incorrect logicalFilePath


Solution

  • How does it work:

    From Liquibase docs:

    logicalFilePath - Use to override the file name and path when creating the unique identifier of change sets. Required when moving or renaming change logs.

    Liquibase calculates the MD5 checksum of the changeSet based on the:

    • content of the changeSet;
    • id of the changeSet;
    • author of the changeSet;
    • path and name of your changeLog file or logicalFilePath;

    If you don't change anything in your changeSet and just try to rerun it, Liquibase will look at databasechangelog.id, databasechangelog.author, databasechangelog.FILENAME and databasechangelog.MD5SUM, and if everything is the same as it was, then the changeSet will be skipped.

    If you change the content of the changeSet, liquibase will throw an error that checksum was changed (while databasechangelog.id, databasechangelog.author and databasechangelog.FILENAME stays the same).

    If you change the id, author or path (logicalFilePath), then Liquibase will think that it's a new changeSet and will try to execute it.

    Why do you have a problem:

    Liquibase treats you changeSet as new, and as you have an error:

    update failed with an error of duplicate column

    I suppose you don't have any preConditions in your changeSet or they aren't sufficient enough,

    How do you fix it:

    So since liquibase thinks that you're executing a new changeSet, nothing stops you from writing those:

    <preConditions onFail="MARK_RAN">
        <not>
            <columnExists tableName="your_table" columnName="your_column"/>
        </not>
    </preConditions>
    

    and because your_table.your_column already exists in the database, then this changeSet will be marked as databasechangelog.EXECTYPE=MARK_RAN and skipped.

    Problem solved!