Don't store classpath: prefix in databasechangelog
Description
Environment
Spring
Activity
Pierrick Rouxel January 26, 2021 at 1:00 PMEdited
I experience the same issue.
It works with this config of gradle plugin:
liquibase {
activities {
main {
changeLogFile '/db/changelog/db.changelog-master.yaml'
classpath "$projectDir/src/main/resources/"
}
}
}
Spring writes filename with the classpath: prefix but it’s ignored by spring integration and cli.
Ruslan Stelmachenko January 27, 2020 at 4:24 AM
@Nathan I can try to create a PR to implement this behavior. But please tell me if you agree with the approach. What do you think about setting a filename with already stripped classpath:
prefix right into filePath
field of ChangeSet
class (if logical file name is not set, of course)?
Ruslan Stelmachenko January 27, 2020 at 4:12 AM
@Dennis SpringLiquibase
class is part of liquibase-core
, so, I think no, it hasn’t to be a Spring issue.
@Nathan I agree this would be best to fix it when it saved. Even earlier: when filename of changeset is calculated. Because, if classpath:
prefix will be removed just before save into DB, it is still possible to catch a filename mismatch when spring runs liquibase update and liquibase tries to match changesets (filename will not match).
There are many bugs related to ignoreClasspathPrefix
flag now. E.e. it doesn’t taken into account when md5sum
is recalculated (after clearCheckSums
). Changesets with classpath:
prefix in their filename
are ignored by maven plugin, and changesets without classpath:
prefix are ignored by spring (leaving md5sum
column as null).
I see this pattern in the code of Liquibase
class at several places (implementations of rollback
method):
DatabaseChangeLog changeLog = getDatabaseChangeLog();
checkLiquibaseTables(false, changeLog, contexts, labelExpression);
changeLog.validate(database, contexts, labelExpression);
changeLog.setIgnoreClasspathPrefix(ignoreClasspathPrefix);
As you can see, the ignoreClasspathPrefix
flag is set to DatabaseChangeLog
only in 4th line. But this DatabaseChangeLog
is passed into checkLiquibaseTables
method earlier. And checkLiquibaseTables
method uses DatabaseChangeLog.getChangeSet
method, which uses normalizePath
method, which checks ignoreClasspathPrefix
flag, which is always false
at this stage, because it is the default value in DatabaseChangeLog
class and because it isn’t updated from Liquibase
class as soon as DatabaseChangeLog
instance is created. Other methods of Liquibase
class doesn’t set ignoreClasspathPrefix
flag on DatabaseChangeLog
at all (update
, changeLogSync
etc).
Maybe changeLog.setIgnoreClasspathPrefix(ignoreClasspathPrefix);
should be run from inside getDatabaseChangeLog()
method? This way it will always return properly initialized DatabaseChangeLog
instance.
So, this classpath:
prefix creates many problems. To get rid of the most of them, I think, the filePath
field of ChangeSet
class should be normalized as soon as possible, e.g. when it calculated for changeset, so the ChangeSet
object will contain already normalized filePath
(if it will not break other functionality, of course). I see now the javadoc of this field as “File changeSet is defined in. May be a logical/non-physical string. It is included in the unique identifier to allow duplicate id+author combinations in different files”. If it can contain a logical name, then it can contain a name without classpath:
prefix also, I think?
P.S. Take care that in Spring, any classpath can have a leading slash (e.g. classpath:/db/changelog.xml
). While it is ignored by spring itself (classpath:/db/changelog.xml
is virtually the same as classpath:db/changelog.xml
) it is not ignored when Liquibase compares ChangeSets. Even after stripping classpath:
prefix the filename can be still different because of this leading slash. So, it would be best to strip this also, if it exist right after classpath:
prefix.
Dennis Effing April 18, 2019 at 8:43 AM
Shouldn't this be a Spring issue? The ignoreClasspathPrefix
property is implemented by Spring itself (and, as far as I can tell, not Liquibase).
I tested running Liquibase using the Liquibase Gradle Plugin and it inserted the database changelog just fine without a classpath:
prefix. However, Spring inserts any database changelog using the prefix. The issue is that while Spring strips the prefix when checking which changes have been run already, the Liquibase CLI does not.
There are two ways to fix this issue:
1. Provide a way to strip the prefix when using the CLI
2. Create a PR for Spring to not use the prefix when saving the filename of the change log
From http://forum.liquibase.org/topic/semantics-of-ignoreclasspathprefix#49382000001495008