Preconditions

Preconditions can be attached to a changelog or a changeset to control the execution of an update based on the state of the database.

There are several reasons to use preconditions, including:

  • Document what assumptions the writers of the changelog had when creating it.
  • Enforce that those assumptions are not violated by users running the changelog.
  • Perform data checks before performing an unrecoverable change such as dropTable.
  • Control what changesets are run and not run based on the state of the database.

If desired, a precondition can be the only tag in a <changeSet>.

Preconditions at the changelog level apply to all changesets, not just those listed in the current changelog or its child changelog.

Sample with preconditions

                
                    <?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"  
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
         http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd">  
    <preConditions>  
        <dbms  type="oracle"  />  
        <runningAs  username="SYSTEM"  />  
    </preConditions>  

    <changeSet  id="1"  author="bob">  
        <preConditions  onFail="WARN">  
            <sqlCheck  expectedResult="0">select count(*) from oldtable</sqlCheck>  
        </preConditions>  
        <comment>Comments should go after preCondition. If they are before then liquibase usually gives error.</comment>  
        <dropTable  tableName="oldtable"/>  
    </changeSet>  
</databaseChangeLog>
            

The above changelog will only run if the database executed against is Oracle and the database user executing the script is “SYSTEM”. It will also only run dropTable if there are no values in the “oldtable”.

Handling failures and errors

Liquibase distinguishes between precondition “failures” (check failed) and “errors” (exception thrown in execution of check) and the reaction to both can be controlled via the “onFail” and “onError” attributes on the <preConditions> tag. Since 1.8

Available attributes

Attribute Description
onFail What to do when preconditions fail (see below).
onError What to do when preconditions error (see below).
onSqlOutput What to do in updateSQL mode (see below). Since 1.9.5
onFailMessage Custom message to output when preconditions fail. Since 2.0
onErrorMessage Custom message to output when preconditions fail. Since 2.0

Possible onFail/onError values

Value Description
HALT Immediately halt the execution of the entire changelog. [DEFAULT]
CONTINUE Skip over the changeset. Execution of the change set will be attempted again on the next update. Continue with the changelog.
MARK_RAN Skip over the changeset, but mark it as executed. Continue with the changelog.
WARN Output a warning and continue executing the changeset / changelog as normal.

Outside a changeset (e.g. at the beginning of the change log), only HALT and WARN are possible values.

Possible onSqlOutput values

Value Description
TEST Run the changeset in updateSQL mode.
FAIL Fail the preCondition in updateSQL mode.
IGNORE Ignore the preCondition in updateSQL mode (default).

AND/OR/NOT logic

Conditional logic can be applied to preconditions using nestable <and>, <or> and <not> tags. If no conditional tags are specified, it defaults to AND.

Examples:

                    <preConditions  onFail="WARN">  
        <dbms  type="oracle"  />  
        <runningAs  username="SYSTEM"  />  
    </preConditions>
            

Will check that the update is running on Oracle AND with the SYSTEM user, but will only generate a warning if the precondition fails.

                 <preConditions>  
     <dbms  type="oracle"  />  
     <dbms  type="mysql"  />  
 </preConditions>
            

Will require running on Oracle AND MySQL, which will always be false, unless a huge and unexpected merger takes place.

                 <preConditions>  
     <or>  
         <dbms  type="oracle"  />  
         <dbms  type="mysql"  />  
     </or>  
 </preConditions>
            

Will require running on Oracle OR MySQL which makes more sense than the above example.

                 <preConditions>  
     <or>  
         <and>  
            <dbms  type="oracle"  />  
            <runningAs  username="SYSTEM"  />  
         </and>  
         <and>  
            <dbms  type="mssql"  />  
            <runningAs  username="sa"  />  
         </and>  
     </or>  
 </preConditions>
            

Will require running as SYSTEM if executing against an Oracle database or running as SA if running against a MS-SQL database.

Available preconditions

<dbms>

Passes if the database executed against matches the type specified.

Available attributes

Attribute Description
type Type of database expected. Multiple dbms values can be specified using comma separated values. required

<runningAs>

Passes if the database user executed under matches the username specified.

Available attributes

Attribute Description
username Database user script is expected to run as. required

<changeSetExecuted>

Passes if the specified changeset has already been executed. Since 1.8

Available attributes

Attribute Description
id Change set "id". required
author Change set "author". required
changeLogFile File name (including classpath relative path) of changeset. required

<columnExists>

Passes if the specified column exists in the database. Since 1.8

Available attributes

Attribute Description
schemaName Name of the table's schema. required
tableName Name of the column's table. required
columnName Name of column. required

<tableExists>

Passes if the specified table exists in the database. Since 1.8

Available attributes

Attribute Description
schemaName Name of the table's schema. required*
tableName Name of the table. required

<viewExists>

Passes if the specified view exists in the database. Since 1.8

Available attributes

Attribute Description
schemaName Name of the view's schema. required
viewName Name of the view. required

<foreignKeyConstraintExists>

Passes if the specified foreign key exists in the database. Since 1.8

Available attributes

Attribute Description
schemaName Name of the foreign key's schema. required
foreignKeyName Name of the foreign key. required

<indexExists>

Passes if the specified index exists in the database. Since 1.8

Available attributes

Attribute Description
schemaName Name of the index's schema. required
indexName Name of the index. required

<sequenceExists>

Passes if the specified sequence exists in the database. Since 1.8

Available attributes

Attribute Description
schemaName Name of the sequences' schema. required
sequenceName Name of the sequence. required

<primaryKeyExists>

Passes if the specified primary key exists in the database. Since 1.8

Available attributes

Attribute Description
schemaName Name of the primary key's schema.
primaryKeyName Name of the primary key. tableName OR primaryKeyName required
tableName Name of the table containing primary key. tableName OR primaryKeyName requiredSince 1.9

<sqlCheck>

Executes an SQL string and checks the returned value. The SQL must return a single row with a single value. To check numbers of rows, use the “count” SQL function. To check for ranges of values, perform the check in the SQL and return a value that can be easily compared against.

                
                    <sqlCheck  expectedResult="1">SELECT COUNT(1) FROM pg_tables WHERE TABLENAME = 'myRequiredTable'</sqlCheck>
            

Available attributes

Attribute Description
expectedResult Value to compare the SQL result to. required

<changeLogPropertyDefined>

Checks whether given changelog parameter is present. If a value is also given, it only fails, if the value is not the same as given. Since 2.0

                
                    <changeLogPropertyDefined  property="myproperty"/>  
<changeLogPropertyDefined  property="myproperty"  value="requiredvalue"/>
            

Available attributes

Attribute Description
property Name of the property to check for. required
value Required value for given property.

<customPrecondition>

Custom preconditions can be created by creating a class that implements the liquibase.precondition.CustomPrecondition interface. Parameters on custom classes are set through reflection based on the <param> sub-tags. Parameters are passed as strings to the custom precondition.

                
                    <customPrecondition  className="com.example.CustomTableCheck">  
    <param  name="tableName"  value="our_table"/>  
    <param  name="count"  value="42"/>  
</customPrecondition>
            

Available attributes

Attribute Description
className Name of custom precondition class. required

Available sub-tags

Attribute Description
param Parameter to pass to the custom precondition.
Available “param” sub-tag Attributes
Attribute Description
name Name of the parameter to set. required
value String value to set parameter to. required

Implementation notes

Preconditions are checked at the beginning of the execution of a particular changelog. If you use the “include” tag and only have preconditions on the child changelog, those preconditions will not be checked until the migrator reaches that file. This behavior may change in future releases, so don't rely on this behavior.