Unexpected error running liquibase precondition failed

Precondition Processing Does not Seem to Work as Documented #1889 Comments Environment Liquibase Version: 4.3.1 Liquibase Integration & Version: Gradle Liquibase Extension(s) & Version: N/A Database Vendor & Version: PostgreSQL 13.2 Operating System Type & Version: Mac OS 10.15.7 Description Note: Preconditions are checked at the beginning of the execution of a particular changelog. […]

Содержание

  1. Precondition Processing Does not Seem to Work as Documented #1889
  2. Comments
  3. Environment
  4. Description
  5. Steps To Reproduce
  6. Actual Behavior
  7. Expected/Desired Behavior
  8. Additional Context
  9. How to fail Liquibase changeset when prerequisites are missing
  10. Precondition 1 — enough disk space
  11. Precondition 2 — specific user
  12. Precondition 3 — complex case
  13. Summary
  14. Preconditions
  15. Syntax
  16. Multiple preconditions
  17. Examples
  18. Handling failures and errors
  19. PostgreSQL Precondition sequenceExists is not working. #1163
  20. Comments
  21. Environment
  22. Description
  23. Steps To Reproduce
  24. Actual Behavior
  25. Expected/Desired Behavior

Precondition Processing Does not Seem to Work as Documented #1889

Environment

Liquibase Version: 4.3.1

Liquibase Integration & Version: Gradle

Liquibase Extension(s) & Version: N/A

Database Vendor & Version: PostgreSQL 13.2

Operating System Type & Version: Mac OS 10.15.7

Description

Note: 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. However, this behavior may change in future releases.

I have encountered a situation in which this is not the case.

Steps To Reproduce

  1. Create an empty databaseChangeLog in a file called database-changelog-master.yml
  2. Create a new databaseChangeLog in a file called add_version_table.yml
  3. Update add_version_table.yml to define the structure for a very simple table called ‘version’
  4. Using the include tag, include add_version_table.yml to database-changelog-master.yml
  5. Create a new databaseChangeLog in a file called rename_version_table.yml
  6. Update rename_version_table.yml to rename the ‘version’ table to ‘version_legacy’
  7. Using the include tag, include rename_version_table.yml to the end of database-changelog-master.yml
  8. Create a new databaseChangeLog in a file called remove_version_legacy_table.yml
  9. Update remove_version_legacy_table.yml to drop the ‘version_legacy’ table
  10. Add a precondition to remove_version_legacy_table.yml which performs a sqlCheck to ensure that the ‘version_legacy’ table is empty before dropping.
  11. Using the include tag, include remove_version_legacy_table.yml to the end of database-changelog-master.yml

Actual Behavior

If liquibase update is run every time a new file is included in ‘database-changelog-master.yml’ (which would be after steps 4, 7, and 11 above) everything works as expected. If liquibase is run against a fresh database or a database which already has a version table (at any step in the process above prior to step 7) the above process fails with the following error:

[2021-06-07 12:43:45] SEVERE [liquibase.integration] Unexpected error running Liquibase: Validation Failed:
1 preconditions generated an error
src/main/resources/database-changelog-master.yml : liquibase.precondition.core.SqlPrecondition4e28bdd1 : > ERROR: relation «version_legacy» does not exist
Position: 32

Expected/Desired Behavior

The behavior which is documented should be restored. Alternately, if this change was intentional, the documentation should be updated and preconditions should be updated to allow users to specify that a precondition should not be checked until the migrator reaches that file.

Additional Context

The case illustrated above is a simplified version of what I am attempting to accomplish in real life. I have an existing table, and new functionality requires me to refactor that table to have a subset of its data residing in related tables. In order to do this with the lowest possible risk of data loss I am first renaming the table, then creating the tables that will replace it, then migrating the data, and only once I have checked that the new table has the same number of rows as the old table (using a sqlCheck precondition) do I want to delete the old table.

The text was updated successfully, but these errors were encountered:

Источник

How to fail Liquibase changeset when prerequisites are missing

How to fail Liquibase changeset when prerequisites are missing

Index creation should not start when there is not enough disk space. An old table should not be dropped until it is completely empty. A database upgrade may happen only if a DBA has changed a default value of some setting. Many database upgrades have some prerequisites. If they are not met, the whole change should not even start. You may write a checklist during development and follow it on the upgrade but there is a better way — preconditions in Liquibase.

Precondition 1 — enough disk space

Data migration estimated for 10 hours that fails after 8 hours because of running out of disk space is a disaster. It would be very inconvenient even if rolling back was easy. Otherwise it could be catastrophic.

Assuming that a changeset that does the migration should not even start without at least 10 GB of free space in a datafile, the following Liquibase file presents a solution.

When the data file has more than 10GB of free space, the changeset executes, but when the precondition fails, you can see the following in the command line.

Remember about setting logLevel , otherwise you will not see a meaningful error.

Precondition 2 — specific user

A database upgrade may require special privileges. Running it without them will fail so you have already created a special user on all environments and you would like to make sure they are used for all upgrades done by Liquibase. A key is all upgrades. This time the precondition applies to the whole upgrade not only a specific changeset. That is easy because preConditions tag can be applied inside a changeset but also inside a databaseChangeLog.

In that case, the precondition is verified before any changeset is attempted to apply. It becomes a prerequisite for the whole database upgrade.

Notice also that there is a predefined tag runningAs which checks the name of the database user who is running the upgrade. There are more such predefined conditions listed in the Liquibase manual.

Precondition 3 — complex case

One precondition per changeset would be too easy if it was always enough. Real life scenarios often require a little bit more. Fortunately, preConditions tag may contain multiple prerequisites joined together with logical operators.

The above example will allow the upgrade only when it is run by dba user, there is at least 10GB of space in the datafile and the upgrade is executed on SQL Server or Oracle.

Summary

Prerequisites are very common in database upgrades. We usually create checklists and manually verify them which is error prone. At some point it makes sense to automate at least some of them. The less items you have to remember about, the more creative your work can become. Preconditions are there to help you.

If anything goes wrong, the upgrade can be rolled back with autorollback feature.

If you are new to Liquibase watch my introduction to Liquibase and find more Liquibase articles.

Would you like to learn db performance? Enroll to my course on Udemy.

50% discount only till February 2. Hurry up!

Источник

Preconditions

Preconditions are changelog or changeset tags which control the execution of an update based on the state of the database.

You can use preconditions to:

  • Document what assumptions the author 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 changeset s are run and not run based on the state of the database.

You can use all Liquibase preconditions in XML, YAML, and JSON changelog s. The only supported precondition for SQL changelog s is sqlCheck . For a list of preconditions, see Available preconditions.

Syntax

You can use one

The sample changelog will only be run if the database executed against is Oracle and the database user executing the script is SYSTEM. Also, it will run the changeset with the sqlCheck precondition and dropTable.

If the preconditions check fails, you will receive a warning and it will continue executing the changeset as normal because of the onFail=»WARN» precondition. To prevent the execution of the changeset when the precondition fails, you can set HALT or CONTINUE values. For more information, see onFail/onError values.

Multiple preconditions

In XML, JSON, and YAML changelog s, you can set multiple preconditions in one

Examples

The following syntax example will check that the update is running on Oracle and with the SYSTEM user but will only generate a warning message if the precondition fails:

The following will require running the changelog on Oracle or MySQL:

You can also see the precondition running as SYSTEM if executing against an Oracle database or running as SA if executing against a MS SQL database.

Handling failures and errors

Liquibase defines two types of preconditions:

  • Precondition failures which represent that the check failed
  • Precondition errors that are the exceptions thrown in the execution of a check

The process of both can be controlled through the onFail and onError attribute s on the type – the type of database expected. Multiple dbms values can be specified using comma-separated values. (required) changeLogPropertyDefined

Checks whether given changelog attribute is present. It fails if the value is not the same as given.

  • property – the name of the property to check. (required)
  • value – the required value for a given property.

changeSetExecuted

Defines if the specified changeset has already been executed.

  • Id – the changeset id. (required)
  • author – the changeset author. (required)
  • changelog-file – the file name (including searchPath relative path) of the changeset . (required)

sqlCheck

Executes an SQL string and checks the returned value. The SQL must return a single row with a single value.

  • To check a number 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.

expectedResult – the value to compare the SQL result to. (required) rowCount

Checks that the number of rows in a table matches an expected value.

  • expectedRows – the number of rows the user expects in a table (required)
  • tableName – the name of the column’s table (required)

runningAs

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

username – the database user script which is expected to run as. (required) columnExists

Defines if the specified column exists in the database.

  • schemaName – the name of the table’s schema.
  • tableName – the name of the column’s table. (required)
  • columnName – the name of the column. (required)

foreignKeyConstraintExists

Defines if the specified foreign key exists in the database.

  • schemaName – the name of the foreign key’s schema.
  • foreignKeyName – the name of the foreign key. (required)

indexExists

Defines if the specified index exists in the database. You can either specify the indexName attribute or tableName and columnNames attribute s.

Note: There are a few databases where the indexName is not unique, that’s why both indexName and tableName can be used.

  • schemaName – the name of the index’s schema.
  • indexName – the name of the index.
  • tableName – the name of the table.
  • columnNames – the name of the column.

primaryKeyExists

Defines if the specified primary key exists in the database.

  • schemaName – the name of the primary key’s schema.
  • primaryKeyName – the name of the primary key constraint.
  • tableName – the name of the table containing primary key.
    ( tableName or primaryKeyName is required)

sequenceExists

Defines if the specified sequence exists in the database.

  • schemaName – the name of the sequences’ schema.
  • sequenceName – the name of the sequence. (required)

tableExists

Defines if the specified table exists in the database.

  • schemaName – the name of the table’s schema.
  • tableName – the name of the table. (required)

uniqueConstraintExists

Checks for the existence of unique constraints before running the update. (since Liquibase 4.9.0)

  • constraintName – the name of the unique constraint
  • tableName – the name of the column’s table (required)
  • columnNames – the name of the column

viewExists

Defines if the specified view exists in the database.

  • schemaName – the name of the view’s schema.
  • viewName – the name of the view. (required)

customPrecondition

Can be created by adding a class that implements the liquibase.precondition.CustomPrecondition interface. Parameters on custom classes are set through reflection based on the

sub-tags. Pass parameters as strings to the custom precondition.

className – the name of the custom precondition class. (required)

The customPrecondition sub-tags:

Источник

PostgreSQL Precondition sequenceExists is not working. #1163

Environment

Liquibase Version: 3.6.2

Liquibase Integration & Version: spring boot

Liquibase Extension(s) & Version: n/a

Database Vendor & Version: PostgreSQL 10.6

Operating System Type & Version: Ubuntu 10.6-0ubuntu0.18.04.1

Description

Precondition sequenceExists failed to check existing auto increment sequence.
Application log:
2018-12-28 17:20:30.539 INFO 13697 — [ main] liquibase.executor.jvm.JdbcExecutor : SELECT c.relname AS SEQUENCE_NAME FROM pg_class c join pg_namespace on c.relnamespace = pg_namespace.oid WHERE c.relkind=’S’ AND nspname = ‘null’ AND c.oid not in (select d.objid FROM pg_depend d where d.refobjsubid > 0)
2018-12-28 17:20:30.544 INFO 13697 — [ main] liquibase.executor.jvm.JdbcExecutor : SELECT c.relname AS SEQUENCE_NAME FROM pg_class c join pg_namespace on c.relnamespace = pg_namespace.oid WHERE c.relkind=’S’ AND nspname = ‘public’ AND c.oid not in (select d.objid FROM pg_depend d where d.refobjsubid > 0)
2018-12-28 17:20:30.548 INFO 13697 — [ main] liquibase.executor.jvm.JdbcExecutor : CREATE SEQUENCE public.analytics_queue_id_seq START WITH 1 INCREMENT BY 1
2018-12-28 17:20:30.550 ERROR 13697 — [ main] liquibase.changelog.ChangeSet : Change Set changelogs/6.xml::11::931@company.ru failed. Error: ERROR: relation «analytics_queue_id_seq» already exists [Failed SQL: CREATE SEQUENCE public.analytics_queue_id_seq START WITH 1 INCREMENT BY 1]

Steps To Reproduce

  1. Apply DDL from db_ddl.txt
  2. Create sequence manually: CREATE SEQUENCE public.analytics_queue_id_seq START WITH 1 INCREMENT BY 1
  3. Run the changeset migration.

Actual Behavior

Precondition passed successfully.
Applying changeset failed, because sequence already exists.

Expected/Desired Behavior

Precondition should be failed.
Changeset should be marked as RAN and should not be applied.

The text was updated successfully, but these errors were encountered:

Источник

checklistIndex creation should not start when there is not enough disk space. An old table should not be dropped until it is completely empty. A database upgrade may happen only if a DBA has changed a default value of some setting. Many database upgrades have some prerequisites. If they are not met, the whole change should not even start. You may write a checklist during development and follow it on the upgrade but there is a better way — preconditions in Liquibase.

Precondition 1 — enough disk space

Data migration estimated for 10 hours that fails after 8 hours because of running out of disk space is a disaster. It would be very inconvenient even if rolling back was easy. Otherwise it could be catastrophic.

Assuming that a changeset that does the migration should not even start without at least 10 GB of free space in a datafile, the following Liquibase file presents a solution.

<?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:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
        
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd
        http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd"
>

    <

changeSet id="Data migration" author="DBA presents">
        <
preConditions onFailMessage="Preconditions failed - at least 10GB of free space">
            <
sqlCheck expectedResult="1">
                select case when size/128.0 - cast(fileproperty(name, 'SpaceUsed') as int)/128.0 > 10000 then 1 else 0 end
                from sys.database_files
                where name = 'MyDatabase'
            </
sqlCheck>
        </
preConditions>

        <
sql>
            insert into NEWTABLE (ID, NAME, VALUE)
            select ID, NAME, VALUE
            from OLDTABLE;
        </
sql>
    </
changeSet>

</

databaseChangeLog>

When the data file has more than 10GB of free space, the changeset executes, but when the precondition fails, you can see the following in the command line.

liquibase --driver=com.microsoft.sqlserver.jdbc.SQLServerDriver --classpath="c:Program FilesMicrosoft JDBC Driver 4.0 for SQL Serversqljdbc_4.0enusqljdbc4.jar" --changeLogFile=databaseChangeLog.xml --url="jdbc:sqlserver://localhostDEVSQL01;databaseName=MyDatabase" --username=sa --password=xyz --logLevel="severe" update
SEVERE 29.07.2018, 14:18: liquibase: databaseChangeLog.xml: databaseChangeLog.xml::Data migration::DBA presents: Change Set databaseChangeLog.xml::Data migration::DBA presents failed. Error: Migration failed for change set databaseChangeLog.xml::Data migration::DBA presents:
Reason:
databaseChangeLog.xml : Preconditions failed - at least 10GB of free space

liquibase.exception.MigrationFailedException: Migration failed for change set databaseChangeLog.xml::Data migration::DBA presents:
Reason:
databaseChangeLog.xml : Preconditions failed - at least 10GB of free space

at liquibase.changelog.ChangeSet.execute(ChangeSet.java:463)
at liquibase.changelog.visitor.UpdateVisitor.visit(UpdateVisitor.java:43)
at liquibase.changelog.ChangeLogIterator.run(ChangeLogIterator.java:70)
at liquibase.Liquibase.update(Liquibase.java:195)
at liquibase.Liquibase.update(Liquibase.java:174)
at liquibase.integration.commandline.Main.doMigration(Main.java:997)
at liquibase.integration.commandline.Main.run(Main.java:170)
at liquibase.integration.commandline.Main.main(Main.java:89)
Caused by: liquibase.exception.PreconditionFailedException: Preconditions Failed
at liquibase.precondition.core.PreconditionContainer.check(PreconditionContainer.java:220)
at liquibase.changelog.ChangeSet.execute(ChangeSet.java:449)
... 7 more
Unexpected error running Liquibase: Preconditions Failed

SEVERE 29.07.2018, 14:18: liquibase: databaseChangeLog.xml::Data migration::DBA presents: Preconditions Failed
liquibase.exception.MigrationFailedException: Migration failed for change set databaseChangeLog.xml::Data migration::DBA presents:
Reason:
databaseChangeLog.xml : Preconditions failed - at least 10GB of free space

at liquibase.changelog.ChangeSet.execute(ChangeSet.java:463)
at liquibase.changelog.visitor.UpdateVisitor.visit(UpdateVisitor.java:43)
at liquibase.changelog.ChangeLogIterator.run(ChangeLogIterator.java:70)
at liquibase.Liquibase.update(Liquibase.java:195)
at liquibase.Liquibase.update(Liquibase.java:174)
at liquibase.integration.commandline.Main.doMigration(Main.java:997)
at liquibase.integration.commandline.Main.run(Main.java:170)
at liquibase.integration.commandline.Main.main(Main.java:89)
Caused by: liquibase.exception.PreconditionFailedException: Preconditions Failed
at liquibase.precondition.core.PreconditionContainer.check(PreconditionContainer.java:220)
at liquibase.changelog.ChangeSet.execute(ChangeSet.java:449)
... 7 more


For more information, use the --logLevel flag

Remember about setting logLevel, otherwise you will not see a meaningful error.

Precondition 2 — specific user

A database upgrade may require special privileges. Running it without them will fail so you have already created a special user on all environments and you would like to make sure they are used for all upgrades done by Liquibase. A key is all upgrades. This time the precondition applies to the whole upgrade not only a specific changeset. That is easy because preConditions tag can be applied inside a changeset but also inside a databaseChangeLog.

<?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:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
       
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd
        http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd"
>

    <

preConditions onFailMessage="Preconditions failed - dba user is required">
        <
runningAs username="dba" />
    </
preConditions>

    <

changeSet id="Data migration" author="DBA presents">
        <
sql>
            insert into NEWTABLE (ID, NAME, VALUE)
            select ID, NAME, VALUE
            from OLDTABLE;
        </
sql>
    </
changeSet>

</

databaseChangeLog>

liquibase --driver=com.microsoft.sqlserver.jdbc.SQLServerDriver --classpath="c:Program FilesMicrosoft JDBC Driver 4.0 for SQL Serversqljdbc_4.0enusqljdbc4.jar" --changeLogFile=databaseChangeLog.xml --url="jdbc:sqlserver://localhostDEVSQL01;databaseName=MyDatabase" --username=sa --password=xyz --logLevel="severe" update
Unexpected error running Liquibase: Validation Failed:
1 preconditions failed
databaseChangeLog.xml : Preconditions failed - dba user is required


SEVERE 29.07.2018, 14:50: liquibase: Validation Failed:
1 preconditions failed
databaseChangeLog.xml : Preconditions failed - dba user is required

liquibase.exception.ValidationFailedException: Validation Failed:
1 preconditions failed
databaseChangeLog.xml : Preconditions failed - dba user is required

at liquibase.changelog.DatabaseChangeLog.validate(DatabaseChangeLog.java:181)
at liquibase.Liquibase.update(Liquibase.java:191)
at liquibase.Liquibase.update(Liquibase.java:174)
at liquibase.integration.commandline.Main.doMigration(Main.java:997)
at liquibase.integration.commandline.Main.run(Main.java:170)
at liquibase.integration.commandline.Main.main(Main.java:89)


For more information, use the --logLevel flag

In that case, the precondition is verified before any changeset is attempted to apply. It becomes a prerequisite for the whole database upgrade.

Notice also that there is a predefined tag runningAs which checks the name of the database user who is running the upgrade. There are more such predefined conditions listed in the Liquibase manual.

Precondition 3 — complex case

One precondition per changeset would be too easy if it was always enough. Real life scenarios often require a little bit more. Fortunately, preConditions tag may contain multiple prerequisites joined together with logical operators.

<?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:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
       
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd
        http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd"
>

    <

preConditions onFailMessage="Preconditions failed - dba user, 10GB of free space and MSSQL or Oracle db engine">
        <
and>
            <
runningAs username="dba" />
            <
sqlCheck expectedResult="1">
                select case when size/128.0 - cast(fileproperty(name, 'SpaceUsed') as int)/128.0 > 10000 then 1 else 0 end
                from sys.database_files
                where name = 'MyDatabase'
            </
sqlCheck>
            <
or>
                <
dbms type="mssql" />
                <
dbms type="oracle" />
            </
or>
        </
and>

    </

preConditions>

    <

changeSet id="Data migration" author="DBA presents">
        <
sql>
            insert into NEWTABLE (ID, NAME, VALUE)
            select ID, NAME, VALUE
            from OLDTABLE;
        </
sql>
    </
changeSet>

</

databaseChangeLog>

The above example will allow the upgrade only when it is run by dba user, there is at least 10GB of space in the datafile and the upgrade is executed on SQL Server or Oracle.

Summary

Prerequisites are very common in database upgrades. We usually create checklists and manually verify them which is error prone. At some point it makes sense to automate at least some of them. The less items you have to remember about, the more creative your work can become. Preconditions are there to help you.

If anything goes wrong, the upgrade can be rolled back with autorollback feature.

If you are new to Liquibase watch my introduction to Liquibase and find more Liquibase articles.

Environment

Liquibase Version:

 ./bin/liquibase --version
####################################################
##   _     _             _ _                      ##
##  | |   (_)           (_) |                     ##
##  | |    _  __ _ _   _ _| |__   __ _ ___  ___   ##
##  | |   | |/ _` | | | | | '_  / _` / __|/ _   ##
##  | |___| | (_| | |_| | | |_) | (_| __   __/  ##
##  _____/_|__, |__,_|_|_.__/ __,_|___/___|  ##
##              | |                               ##
##              |_|                               ##
##                                                ## 
##  Get documentation at docs.liquibase.com       ##
##  Get certified courses at learn.liquibase.com  ## 
##  Free schema change activity reports at        ##
##      https://hub.liquibase.com                 ##
##                                                ##
####################################################
Starting Liquibase at 17:02:20 (version 4.7.0 #1140 built at 2022-01-07 19:26+0000)
Liquibase Version: 4.7.0
Liquibase Community 4.7.0 by Liquibase
Running Java under /usr/java/jdk1.8.0_144/jre (Version 1.8.0_144)

Note: The same Liquibase changeSet worked in Liquibase Version: 3.10.2

Liquibase Integration & Version: CLI

Liquibase Extension(s) & Version:

Database Vendor & Version:

> SELECT version();
                                   version                                    
------------------------------------------------------------------------------
 PostgreSQL 10.13 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.9.3, 64-bit

Operating System Type & Version:

$ cat /etc/os-release 
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"

CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"

Description

foreignKeyConstraintExists doesn’t seem to be working.
I’m trying to MARK_RAN if foreignKey already exists so I just create the FK if it is absent.

This was working with Liquibase 3.10.2. I’m in the process of upgrading it in order to improve performance as a simple migration took multiple hours with the previous version.

Steps To Reproduce

<changeSet ...>
  <preConditions onFail="MARK_RAN">
    <not>
      <foreignKeyConstraintExists foreignKeyName="FK_NAME"/>
    </not>
  </preConditions>
  <addForeignKeyConstraint constraintName="FK_NAME"  ... />
</changeSet>

Actual Behavior

changeSet execution fails trying to execute the addForeignKeyConstraint (since the constraint with that name already exists).

Expected/Desired Behavior

The precondition onFail should’ve been called (adding the changeSet as MARK_RAN to liquibase changelog), and, as such, the changeSet shouldn’t execute addForeignKeyContstraint.

Additional Context

I was able to implement a temporary workaround specific to the database vendor that confirms that the FK exists.

        <preConditions onFail="MARK_RAN">
            <sqlCheck expectedResult="0">
              SELECT COUNT(*) FROM information_schema.table_constraints WHERE constraint_name='FK_NAME' AND table_schema = '<x>'
            </sqlCheck>
        </preConditions>

From my (limited) understanding, Liquibase does some sort of DB snapshot, which it uses to verify some of these pre-conditions (not the sqlCheck) and it doesn’t seem to be importing the ForeignKey to that snapshot.

In our previous blog post, we discussed how we can apply different changelogs to different database environments. It is more than often, that when applying a changelog, changeset writer assumes database in a certain state. Like when you are adding a column to the database, you would assume that corresponding table is present. Or when you are dropping a table, it has no data in it. Or we assume that underlying database connection is of a particular nature. We can check for and decide what to do by using the concept of Preconditions in the Liquibase. Using preconditions allows to validate underlying assumption and decide the course of action. Preconditions can be attached to changelogs or changesets to control the execution of an update based on the state of the database.

Types of Preconditions and their meaning

Precondition Name Checks Performed
dbms Checks if the script is running against a specific DBMS (e.g. SQL Server or Oracle)
runningAs Checks if the script is running as a specific database user
changesetExecuted Checks if the changeset has already been executed
tableExists Checks if a table exists in the database
columnExists Checks if a column exists in a table in the database
viewExists Checks if a view exists in the database
indexExists Checks if a index exists in the database
foreignKeyConstraintExists Checks if a foreign key exists in a table
primaryKeyExists Checks if a table has the specified primary key
sequenceExists Checks if a sequence exists in the database
sqlCheck Runs a SQL command and checks the output returned. The SQL command must return a single value (one row and column) like the output of COUNT, SUM, MIN, MAX or AVG.

Each of these preconditions can again have one or more attributes which are necessary to define the precondition.

Handling Failures and Errors

Naturally, you would want to take some actions when a precondition fails. There can be two scenarios:

Failure: When the precondition check fails
Error: When the check itself cannot be performed and an exception is thrown

Both of above situations can be handled by a number of attributes. Some of these include:

Attribute Description
onFail What to do when preconditions fail
onError What to do when preconditions error
onUpdateSQL What to do in updateSQL mode
onFailMessage Custom message to output when preconditions fail
onErrorMessage Custom message to output when preconditions fail

Again, now that the underlying Precondition failed or the check for precondition itself failed, you need to decide the course of action. It can be set using below attributes:

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

The possible actions for onUpdateSQL are a little different.

Fitting it all together

In our database, we have a table [dbo].[CustomerDetails] which has only two columns: CustomerTypeID and CustomerDesc. We want to add a new column to this table, say CustomerAddress but we want to make sure that the said table exists before we. For this, we can use below changelog:

Let’s go ahead and apply our change using liquibase update. Since there are no preconditions fails, we’ll not see any indication of same in the logs:

running liquibase update with successful preconditions

Now, let’s try to apply below changelog where we are trying to drop a table:

Here, in the precondition we have specified that table contains no records. However, our table contains 2 records. Let’s run liquibase and see what happens:

running liquibase update with failed preconditions

We can see that liquibase command has failed to proceed as instructed. At this point, if we check records in the DATABASECHANGELOG table, we would see no new entries as no change has been made.

Conditional logic in Preconditions

We can use as many preconditions as we need to make sure underlying assumptions are valid. By default all preconditions are associated by AND. The possible association can be defined using AND, OR and NOT.

Для
бизнеса

  • По заинтересованным лицам

  • IT-лидеры

  • Независимые разработчики ПО

  • Архитекторы корпоративных приложений

  • По отраслям

  • Истории успеха

  • По вариантам использования

  • Модернизация приложений

  • Сокращение расходов на ПО

  • Автоматизация внутренних процессов

Понравилась статья? Поделить с друзьями:
  • Unexpected error running liquibase error no schema has been selected to create in
  • Underverse ost error sans theme
  • Underverse fatal error sans
  • Underverse error sans wiki
  • Underverse error sans vs ink sans