I'm wondering if there is an easy way to create liquibase precondition checking if particular value is NULL or NOT NULL. My approach doesn't work.
I created the following test changeset:
<?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.1.xsd">
<changeSet id="1" author="test">
<createTable tableName="test_table">
<column name="id" type="int">
<constraints primaryKey="true" nullable="false" />
</column>
<column name="test_text" type="varchar(50)">
<constraints nullable="true" />
</column>
</createTable>
</changeSet>
<changeSet id="2" author="test">
<insert tableName="test_table">
<column name="id" value="1" />
<column name="test_text" value="test1" />
</insert>
<insert tableName="test_table">
<column name="id" value="2" />
<column name="test_text" value="null" />
</insert>
</changeSet>
<changeSet id="3" author="test">
<preConditions onFail="HALT">
<sqlCheck expectedResult="test1">select test_text from test_table where id=1</sqlCheck>
</preConditions>
<insert tableName="test_table">
<column name="id" value="3" />
<column name="test_text" value="foo" />
</insert>
</changeSet>
<changeSet id="4" author="test">
<preConditions onFail="HALT">
<sqlCheck expectedResult="NULL">select test_text from test_table where id=2</sqlCheck>
</preConditions>
<insert tableName="test_table">
<column name="id" value="4" />
<column name="test_text" value="bar" />
</insert>
</changeSet>
</databaseChangeLog>
Everything works fine except the changeset 4, which is failing with surprising error message "No rows returned from SQL Precondition". However the data is there:
$ psql -U test -c 'select * from test_table'
id | test_text
----+-----------
1 | test1
2 |
3 | foo
(3 rows)
It seems the result is interpreted as empty. In fact it returns one row with one column containing NULL value. Any ideas how to make this working?
I'm using Liquibase 3.3.5 with PostgreSQL 9.3 on Ubuntu 14.04.
Here is the full output (info level):
$ ./liquibase --driver=org.postgresql.Driver --url="jdbc:postgresql://localhost:5432/test" --username=test --password="" --changeLogFile=/tmp/test.changeset.xml --logLevel=info update
Liquibase Home is not set.
INFO 02.06.15 09:57: liquibase: Successfully acquired change log lock
INFO 02.06.15 09:57: liquibase: Creating database history table with name: public.databasechangelog
INFO 02.06.15 09:57: liquibase: Reading from public.databasechangelog
INFO 02.06.15 09:57: liquibase: /tmp/test.changeset.xml: /tmp/test.changeset.xml::1::test: Table test_table created
INFO 02.06.15 09:57: liquibase: /tmp/test.changeset.xml: /tmp/test.changeset.xml::1::test: ChangeSet /tmp/test.changeset.xml::1::test ran successfully in 15ms
INFO 02.06.15 09:57: liquibase: /tmp/test.changeset.xml: /tmp/test.changeset.xml::2::test: New row inserted into test_table
INFO 02.06.15 09:57: liquibase: /tmp/test.changeset.xml: /tmp/test.changeset.xml::2::test: New row inserted into test_table
INFO 02.06.15 09:57: liquibase: /tmp/test.changeset.xml: /tmp/test.changeset.xml::2::test: ChangeSet /tmp/test.changeset.xml::2::test ran successfully in 10ms
INFO 02.06.15 09:57: liquibase: /tmp/test.changeset.xml: /tmp/test.changeset.xml::3::test: New row inserted into test_table
INFO 02.06.15 09:57: liquibase: /tmp/test.changeset.xml: /tmp/test.changeset.xml::3::test: ChangeSet /tmp/test.changeset.xml::3::test ran successfully in 3ms
SEVERE 02.06.15 09:57: liquibase: /tmp/test.changeset.xml: /tmp/test.changeset.xml::4::test: Change Set /tmp/test.changeset.xml::4::test failed. Error: Migration failed for change set /tmp/test.changeset.xml::4::test:
Reason:
/tmp/test.changeset.xml : No rows returned from SQL Precondition
liquibase.exception.MigrationFailedException: Migration failed for change set /tmp/test.changeset.xml::4::test:
Reason:
/tmp/test.changeset.xml : No rows returned from SQL Precondition
at liquibase.changelog.ChangeSet.execute(ChangeSet.java:485)
at liquibase.changelog.visitor.UpdateVisitor.visit(UpdateVisitor.java:43)
at liquibase.changelog.ChangeLogIterator.run(ChangeLogIterator.java:73)
at liquibase.Liquibase.update(Liquibase.java:200)
at liquibase.integration.commandline.Main.doMigration(Main.java:1044)
at liquibase.integration.commandline.Main.run(Main.java:175)
at liquibase.integration.commandline.Main.main(Main.java:94)
Caused by: liquibase.exception.PreconditionFailedException: Preconditions Failed
at liquibase.precondition.core.AndPrecondition.check(AndPrecondition.java:51)
at liquibase.precondition.core.PreconditionContainer.check(PreconditionContainer.java:201)
at liquibase.changelog.ChangeSet.execute(ChangeSet.java:471)
... 6 more
INFO 02.06.15 09:57: liquibase: /tmp/test.changeset.xml::4::test: Successfully released change log lock
Unexpected error running Liquibase: Preconditions Failed
SEVERE 02.06.15 09:57: liquibase: /tmp/test.changeset.xml::4::test: Preconditions Failed
liquibase.exception.MigrationFailedException: Migration failed for change set /tmp/test.changeset.xml::4::test:
Reason:
/tmp/test.changeset.xml : No rows returned from SQL Precondition
at liquibase.changelog.ChangeSet.execute(ChangeSet.java:485)
at liquibase.changelog.visitor.UpdateVisitor.visit(UpdateVisitor.java:43)
at liquibase.changelog.ChangeLogIterator.run(ChangeLogIterator.java:73)
at liquibase.Liquibase.update(Liquibase.java:200)
at liquibase.integration.commandline.Main.doMigration(Main.java:1044)
at liquibase.integration.commandline.Main.run(Main.java:175)
at liquibase.integration.commandline.Main.main(Main.java:94)
Caused by: liquibase.exception.PreconditionFailedException: Preconditions Failed
at liquibase.precondition.core.AndPrecondition.check(AndPrecondition.java:51)
at liquibase.precondition.core.PreconditionContainer.check(PreconditionContainer.java:201)
at liquibase.changelog.ChangeSet.execute(ChangeSet.java:471)
... 6 more
Might not be the most simple or elegant way but you could use a <customPrecondition>.
You would write your own class that has to implement CustomPrecondition.
Checkout this example class: ExampleCustomPrecondition as a reference.
You get a Database object at hand and could do a query to the database yourself and throw a CustomPreconditionFailedException.
(Alternatively create a Jira-Ticket to file a request for changing this behavior or adding a way to achieve the null preCondition check.)
This workaround might work
<sqlCheck expectedResult="1">select COUNT(id) from test_table where id=2 AND test_text IS NULL</sqlCheck>
Related
I wan't my build script to be set with an environment variable that sets the schema name for the following changeSet:
<createTable tableName="actor" schemaName="mySchema">
<column autoIncrement="true" name="id" type="INTEGER">
<constraints nullable="false" primaryKey="true" primaryKeyName="actor_pkey"/>
</column>
<column name="firstname" type="VARCHAR(255)"/>
<column name="lastname" type="VARCHAR(255)"/>
<column name="twitter" type="VARCHAR(15)"/>
</createTable>
I launch Liquibase from the command line in my build script.
To pass arguments that will change from environment to environment, Liquibase uses property substitution. This requires 2 changes to your existing process.
The first is to replace literal values with a variable:
I want my build script to be set with an environment variable that sets the schema name for the following changeSet (we will use the property name of SCHEMA below):
<createTable tableName="actor" schemaName="${SCHEMA}">
<column autoIncrement="true" name="id" type="INTEGER">
<constraints nullable="false" primaryKey="true" primaryKeyName="actor_pkey"/>
</column>
<column name="firstname" type="VARCHAR(255)"/>
<column name="lastname" type="VARCHAR(255)"/>
<column name="twitter" type="VARCHAR(15)"/>
</createTable>
Then we can pass a value for SCHEMA during command line creation, and to prove what value will be substituted, I used (note lookup is the name of the schema I want to substitute):
liquibase --changeLogFile=“sample.xml” updateSQL -DSCHEMA=lookup
And here is a snippet of the output which will show that the schema has been replaced with the value of lookup:
....
CREATE TABLE lookup.actor (id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, firstname VARCHAR(255), lastname VARCHAR(255), twitter VARCHAR(15), CONSTRAINT actor_pkey PRIMARY KEY (id));
...
And if that looks okay, you can actually run the changeset by:
liquibase --changeLogFile=“sample.xml” update -DSCHEMA=lookup
Is it possible to create two preconditons for single elements of a changeset? Like generating a column if the changelog file is run at a SQL Server database and generating another column if the changelog file is run at an Oracle database. In a way like this for example:
<changeSet author="Me" id="1528876614155">
<createTable tableName="ELECTRICITY_PRODUCTS">
<preConditions onFail="MARK_RAN" onSqlOutput="TEST">
<dbms type="mssql" />
<column autoIncrement="true" name="EP_ID" type="NUMBER">
</preConditions>
<preConditions onFail="MARK_RAN" onSqlOutput="TEST">
<dbms type="oracle" />
<column autoIncrement="false" name="EP_ID" type="FLOAT">
</preConditions>
</createTable>
</changeset>
It's not possible to create column inside condition.
If I understand your problem, then you have two (maybe more) options:
Separte the changeSets for each dbms
Create property for dbms and use them like:
<property dbms="oracle" name="autoincrement" value="true" />
<property dbms="oracle" name="autoincrementType" value="NUMBER" />
<property dbms="mssql" name="autoincrement" value="false" />
<property dbms="mssql" name="autoincrementType" value="FLOAT" />
<changeSet id="create_a_table" author="system">
<createTable tableName="a_table">
<column name="a_column" autoIncrement="${autoincrement}" type="${autoincrementType}" />
</createTable>
</changeSet>
I didn't tested that, it's just idea how to solve the problem.
This seems to be not possible but another good solution is to create changesets for these single elements. In this example it could be:
<changeSet author="Me" id="1528876614155">
<createTable tableName="DAILY DATA">
<column name="ID" type="NUMBER"/>
</createTable>
</changeset>
<changeSet author="Me" id="1528876614156">
<preConditions onFail="MARK_RAN" onSqlOutput="TEST">
<dbms type="oracle" />
</preConditions>
<addColumn tableName="DAILY_DATA">
<column name="VALUE" type="NUMBER"/>
</addColumn>
</changeset>
<changeSet author="Me" id="1528876614157">
<preConditions onFail="MARK_RAN" onSqlOutput="TEST">
<dbms type="mssql" />
</preConditions>
<addColumn tableName="DAILY_DATA">
<column name="VALUE" type="NUMBER"/>
</addColumn>
</changeset>
I am trying to add a new field with liquibase
<addColumn tableName="contact_client">
<column defaultValue="0"
defaultValueBoolean="0"
name="obsolete"
type="boolean"/>
</addColumn>
But I am getting this error :
liquibase.exception.DatabaseException: Invalid default value for
'obsolete' [Failed SQL: ALTER TABLE myApp.contact_client ADD obsolete
BIT(1) DEFAULT 'false' NULL]
How can I pass a default value?
Actually, here is the solution for a MySQL database:
<addColumn tableName="contact_client">
<column defaultValueBoolean="false"
name="obsolete"
type="boolean"/>
</addColumn>
I needed to remove the defaultValue property.
This is what fixed it for me, thanks to this link, I added the addNotNullConstraint as a next changeSet
<changeSet id="1000001" author="some_author">
<addColumn tableName="some_table">
<column name="some_column" type="boolean"/>
</addColumn>
</changeSet>
<changeSet id="1000002" author="some_author">
<addNotNullConstraint
tableName="some_table"
columnName="some_column"
columnDataType="boolean"
validate="true"
defaultNullValue="1"/>
</changeSet>
I solved this issue by using defaultValue="0"
Source code :
Database value :
NB: I'm using SQL Serve
Hope this will be helpful
I need to add changeSet with only preConditions tag and if it failed then stop execute. This is my changeset:
<?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.3.xsd">
<changeSet id="GEO-6154_2" author="kadzhaev">
<preConditions onFail="HALT">
<sqlCheck expectedResult="0">select count(*) from "public"."messages" where key = 'branding.region.oktmo' and value != '';</sqlCheck>
</preConditions>
</changeSet>
</databaseChangeLog>
but it doesn't work cause:
liquibase: cvc-complex-type.2.4.a: Invalid content was found starting with element 'preConditions'. I think I should add some tag after preConditions but I do not need it. How to solve this problem?
I understand dropwizard expects all DB migrations to be in migrations.xml.
Let's say I'm starting a new project and create my tables:
<changeSet id="1" author="codahale">
<createTable tableName="people">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="fullname" type="varchar(255)">
<constraints nullable="false"/>
</column>
<column name="jobtitle" type="varchar(255)"/>
</createTable>
</changeSet>
At the next iteration I have to add a new column so I ad another changeSet to migrations.xml:
<changeSet id="2" author="dbdemopostgres">
<addColumn tableName="people">
<column name="description" type="varchar(255)">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
Then let's say I have to make a column longer so I add:
<changeSet id="3" author="dbdemopostgres">
<modifyDataType tableName="people" columnName="description" newDataType="varchar(300)"/>
</changeSet>
And so it goes. I keep appending all changes to my migrations.xml which grows indefinitely. On a large project it's just a matter of time when it becomes unmanagable. Does anyone has any recommendations for strategy to maintain ongoing database changes?
You can divide your migrations.xml file to multiple files as documented here apparently.
<?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.1.xsd">
<include file="com/example/db/changelog/db.changelog-1.0.xml"/>
<include file="com/example/db/changelog/db.changelog-1.1.xml"/>
<include file="com/example/db/changelog/db.changelog-2.0.xml"/>
</databaseChangeLog>