The changeset tag is a unit of change that Liquibase executes on a database and which is used to group database Change Types together. A list of changes created by multiple changesets are tracked in a changelog.
A changeset is uniquely tagged by both an
author and an
id attributes (
author:id), as well as the changelog file path. The
id tag is only used as an identifier, it does not direct the order that changes are run and does not have to be an integer. If you do not know or do not want to save the actual author, use a placeholder value such as
UNKNOWN. To execute the changeset, you must include both
<?xml version="1.0" encoding="UTF-8"?> <databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:pro="http://www.liquibase.org/xml/ns/pro" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.0.xsd http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-3.8.xsd"> <changeSet id="1" author="bob"> <comment>A sample change log</comment> <createTable/> </changeSet> <changeSet id="2" author="bob" runAlways="true"> <alterTable/> </changeSet> <changeSet id="3" author="alice" failOnError="false" dbms="oracle"> <alterTable/> </changeSet> <changeSet id="4" author="alice" failOnError="false" dbms="!oracle"> <alterTable/> </changeSet> </databaseChangeLog>
Running the changeset
As Liquibase uses the DATABASECHANGELOG table, it reads the changesets in order and, for each one, checks the DATABASECHANGELOG table to see if the combination of
id/author/filepath has been run.
If it has been run, the changeset will be skipped unless there is a
runAlways tag set to
true in that changeset. After all the changes in the changesets are run, Liquibase will insert a new row with the
id/author/filepath along with an MD5Sum of the changeset in the DATABASECHANGELOG.
Note: filepath is the path that defines the
changeLogFile attribute. Even if the same file is referenced with a different path, that is considered a different file unless the
logicalFilePath is defined.
Liquibase attempts to execute each changeset in a transaction that is committed at the end, or rolled back if there is an error. Some databases will auto-commit statements which interferes with this transaction setup and could lead to an unexpected database state. Therefore, it is best practice to have just one change per changeset unless there is a group of non-auto-committing changes that you want to apply as a transaction such as inserting data.
|id||Specifies an alpha-numeric identifier. Required|
|author||Specifies the creator of the changeset. Required|
|dbms||Specifies the type of a database for which that changeset will be used. When the migration step is running, it checks the database type against this
Note: For more information about database type names, see Supported databases page.
|runAlways||Executes the changeset on every run, even if it has been run before.|
|runOnChange||Executes the changeset the first time and each time the changeset has been changed.|
|Contexts||Controls whether a changeset is executed depending on runtime settings. Any string can be used for the context name and they are checked case-insensitively.|
|Labels||Controls whether a changeset is executed depending on runtime settings. Any string can be used for the label name and they are checked case-insensitively.|
Specifies whether the changeset can be ran as a single transaction (if possible). Default value is
Warning: If this attribute is set to false and an error occurs part way through running a changeset that contains multiple statements, the Liquibase DATABASECHANGELOG table will be left in an invalid state. Since 1.9
|failOnError||Defines whether the migration will fail if an error occurs while executing the changeset. Default value is
|objectQuotingStrategy||Controls how object names are quoted in the generated SQL or used in calls to the database. Different databases do different things to
the names of objects. For example, Oracle converts everything to uppercase (unless quoted). There are three possible values. The default value is
|runOrder||Overrides the order in the changelog from where the changeset with the
|created||Stores dates, versions, or any other string of value without using remarks (comments) attributes. Since 3.5|
|ignore||Ignores the changeset from the execution. Since 3.6|
|logicalFilePath||Overrides the file name and path when creating the unique identifier of changesets. Required when moving or renaming changelog.|
|comment||Specifies the description of the changeset. XML comments will provide the same benefit. Future releases of Liquibase may be able to make use of <comment> tag comments to generate documentation.|
Must be passed before the changeset will be executed. It is typically used for doing a data sanity check before doing something unrecoverable such as a
Note: For more information, see Preconditions.
|<Any Refactoring Tag(s)>||Specifies the database change(s) to run as part of the changeset (Change Types).|
|validCheckSum||Adds a checksum that is considered valid for this changeset, regardless of what is stored in the database. It is
primarily used when you need to change a changeset and don't want errors thrown on databases on which it has already been run (not a recommended procedure). Special value
Specifies SQL statements or Change Type tags that describe how to rollback the changeset.
Note: For more information, see Liquibase Rollback Workflow.
The rollback tag
The rollback tag describes how to roll back a change using SQL statement(s), change tags, or a reference to a previous changeset.
Rollback tag examples
You can have a plain SQL in the content part of the
<rollback> element. The text in the element is treated as an
<sql> change with
stripComments set to
splitStatements set to
endDelimiter set to
<changeSet id="1" author="bob"> <createTable tableName="testTable"> <rollback> drop table testTable </rollback> </changeSet>
Also, you can have any type of change in the
<changeSet id="1" author="bob"> <createTable tableName="testTable"> <rollback> <dropTable tableName="testTable"/> </rollback> </changeSet>
The following example shows how a
<rollback> element can be referred to another changeset. So, to roll back the changeset with
id="2", apply the changeset with
<changeSet id="2" author="bob"> <dropTable tableName="testTable"/> <rollback changeSetId="1" changeSetAuthor="bob"/> </changeSet>
When Liquibase reaches a changeset, it computes a checksum and stores it in the DATABASECHANGELOG table. The value of storing the checksum for Liquibase is to know if something has been changed in the changeset since it was run.
If the changeset has been changed since it was run, Liquibase will exit the migration with an error message like
Validation failed: change set check sums <changeset identifer> was: <old checksum> but is now: <newchecksum>. This is because Liquibase cannot identify what was changed and the
database may be in a state different than what the changelog is expecting. If there was a valid reason for the changeset to be changed and you want to ignore this error, there are two options.
Note: You can also use the clearCheckSums command to resolve the checksum error, however, it will clear the entire column of checksums in your DATABASECHANGELOG table.
The manual update of the DATABASECHANGELOG table
The first option is to manually update the DATABASECHANGELOG table so that the row with the corresponding
id/author/filepath has a null value for the checksum. You would
need to do this for all environments where the changeset has been deployed. The next time you run the Liquibase
update command, it will update the checksum value to the new correct value.
The second option is to add a
<validCheckSum> element to the changeset. The text contents of the element should contain the old checksum from the error message.
The runOnChange attribute
Checksums are also used in conjunction with the
runOnChange changeset attribute. There are times you may not want to add a new changeset because you only need to know
about the current version, but you want this change to be applied whenever it is updated. For example, you can do this with stored procedures.
If you copy the entire
text of the stored procedure to a new changeset each time you make a change, you will not only end up with a very long changelog, but you will lose the merging and diffing
power of your source control. Instead, put the text of the stored procedure in a changeset with a
runOnChange="true" attribute. The stored procedure will be re-created
when there is a change to the text of it.
Note: For more information, see runOnChange changeset attribute.