I’m a little unsure if this behavior is a bug or if I am simply confused about the behavior of the rollback feature.
If I rollback a changeset, the schema of the database reverts to the state prior to the chageset – which is expected. However, the changeset record stays in the log so it is impossible to reapply the changes without first changing something in the file.
Is there a reason for keeping a record of changesets even if they have been rolled back? Is the behavior I am seeing correct?
Release: Liquibase 1.9.4
Database: Microsoft SQLServer 2005
JVM: IKVM.NET
OS: Windows XP
It should remove it. What rollback command were you using? Count, date, tag?
It may be related to not enough testing on ikvm too.
Has anyone else noticed this behavior? You could create an issue on liquibase.jira.com too.
Nathan
Okay, so I found the issue.
Because of the way our deploy system works, I have to use logicalFilePath in the databaseChangeLogs. Being a windows guy, the form of my entries tends to look something like the following (notice the path delimiters):
Foo\Bar\gaz.xml
This works just fine right up to the following line in removeRanStatus():
sql = sql.replaceFirst("\\?", escapeStringForDatabase(changeSet.getFilePath()));
At this point, replaceFirst treats the windows path delimiter as an escape character and liquibase winds up trying to delete a changeset with the logical name FooBargaz.xml instead of Foo\Bar\gaz.xml. The end result is that the changeset is rolled back, but the entry remains.
I pulled the source and tried the following and it seems to work:
public void markChangeSetAsReRan(ChangeSet changeSet) throws JDBCException {
String dateValue = getCurrentDateTimeFunction();
String sql =
MessageFormat.format(
"UPDATE {0} SET DATEEXECUTED={1}, MD5SUM=''{2}'' WHERE ID=''{3}'' AND AUTHOR=''{4}'' AND FILENAME=''{5}''",
escapeTableName(getDefaultSchemaName(), getDatabaseChangeLogTableName()),
dateValue,
escapeStringForDatabase(changeSet.getMd5sum()),
escapeStringForDatabase(changeSet.getId()),
escapeStringForDatabase(changeSet.getAuthor()),
escapeStringForDatabase(changeSet.getFilePath()));
this.getJdbcTemplate().execute(new RawSqlStatement(sql), new ArrayList());
this.commit();
}
public void removeRanStatus(ChangeSet changeSet) throws JDBCException {
String sql =
MessageFormat.format(
“DELETE FROM {0} WHERE ID=’’{1}’’ AND AUTHOR=’’{2}’’ AND FILENAME=’’{3}’’”,
escapeTableName(getDefaultSchemaName(), getDatabaseChangeLogTableName()),
escapeStringForDatabase(changeSet.getId()),
escapeStringForDatabase(changeSet.getAuthor()),
escapeStringForDatabase(changeSet.getFilePath()));
this.getJdbcTemplate().execute(new RawSqlStatement(sql), new ArrayList());
commit();
getRanChangeSetList().remove(new RanChangeSet(changeSet));
}
I’m not exactly a java wizard, so I’m not sure if there are some side-effects here that might make this approach a bad idea. Regardless, I’ll open a Jira ticket to capture the issue.
Thanks for pointing that out, and for the jira issue. Path separators are always a pain when trying to be cross-platform. We try to convert to use / as the separator if \ is entered, we may have missed converting the logicalFilePath.
Updating your logicalFilePaths and running an sql statement to update your 's to /'s should solve your problem for now.
Nathan