Adding composite unique constraint in Liquibase - liquibase

I'm creating a link table which has 3 columns; id, product_id, tournament_id.
Adding a uniqueConstraint to the "id" column is trivial, but I want to ensure that any pair of (product_id, tournament_id) is unique.
The example at Liquibase.org shows
<changeSet author="liquibase-docs" id="addUniqueConstraint-example">
<addUniqueConstraint catalogName="cat"
columnNames="id, name"
constraintName="const_name"
deferrable="true"
disabled="true"
initiallyDeferred="true"
schemaName="public"
tableName="person"
tablespace="A String"/>
</changeSet>
but is it possible to accomplish this within a <createTable> block?
Also, just to confirm; does this create a composite unique constraint on the two columns, or does it create two separate unique constraints?

You can read liquibase manual also similar problem you can find here
In your case it should be:
<changeSet author="liquibase-docs" id="addUniqueConstraint-example">
<addUniqueConstraint
columnNames="product_id, tournament_id"
constraintName="your_constraint_name"
tableName="person"
/>
</changeSet>

I am pretty certain that:
You can't do it inside the createTable tag itself, but you can do it within the same changeset as when the table is created.
It does create a composite unique constraint on the two columns. One way you can check is to run liquibase with the command to generate the SQL for update rather than running the update command and check what it does for your database. On the command line, rather than running liquibase update you would run liquibase updateSQL.

In case people like myself are still asking themselves this question years later: (by now) it is possible to do this within the createTable tag itself if you provide the same name for the constraint:
<changeSet id="composite-unique-example" author="composite-unique-example">
<createTable tableName="example">
<column name="foo" type="bigint">
<constraints unique="true" uniqueConstraintName="foo-bar_unique"/>
</column>
<column name="bar" type="bigint">
<constraints unique="true" uniqueConstraintName="foo-bar_unique"/>
</column>
</createTable>
</changeSet>
Running Liquibase 4.5.0 and MySQL 5.7:
mysql> insert into example (foo, bar) values (1, 2), (2, 1), (1, 3);
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from example;
+------+------+
| foo | bar |
+------+------+
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
+------+------+
3 rows in set (0.00 sec)
mysql> insert into example (foo, bar) values (1, 2);
ERROR 1062 (23000): Duplicate entry '1-2' for key 'foo-bar_unique'

Related

How to copy the the data from one Table to another Table Using Liquibase scripts

I want to know the solution for the below Test ( to copy the the data from one Table to another Table) using Liquibase a part of Corda.
Case: As part of I want to create a new table into an existing database which has already a table called TableA (which has id, name, value columns) which has some data init, I have created a TableB (which has same id, name, value columns) and I wanted to copy the data from TableA to TableB.
For that I had used the following liquibase script as suggested here in Liquibase and In-order to test I had connected to PostgreSQL DB and connected schema called "corda_schema" which has the tables.
<changeSet author="liquibase.corda" id="update-table">
<update schemaName="corda_schema" tableName="TableB">
<column name="id" valueComputed="(SELECT id from TableA)"/>
<column name="name" valueComputed="(SELECT name from TableA)"/>
<column name="value" valueComputed="(SELECT value from TableA)"/>
</update>
</changeSet>
I was getting the following error when I tried with the Liquibase update script
Error: liquibase.corda failed
Error: schema "CORDA_SCHEMA" not found in SQL statement
If I don't given the schema name in update like this
<update tableName="TableB">
<column name="value" valueComputed="(SELECT value from TableA)"/>
</update>
the Liquibase is searching in the Public schema for TableA and I get this error:
Error: liquibase.corda failed Error: schema "PUBLIC" not found in SQL statement`
And also I tried this Liquibase script changeSet by creating the table itself I tried to update data, this changeSet is running and table is created but data is not copied.
<changeSet author="liquibase.corda" id="update-table">
<createTable schemaName = "corda_schema" tableName="TableB">
<column name="id" valueComputed= "(SELECT id FROM TableA)"/>
</createTable>
</changeSet>
Please suggest anything I am missing or any other usages that will make my test success to get the data from one table to another table.
Thanks in advance.
I would suggest to just use custom sql:
<changeSet author="liquibase.corda" id="insert-table">
<sql>
insert into corda_schema.TableB
select id,name,value from corda_schema.TableA;
</sql>
</changeSet>

Liquibase: Keep changes from script but remove the changelog in changelog table

I am using liquibase for applying database changes. I have below changeset and below are the entries in changelog:
Changeset:
<changeSet author="me" id="1">
<addColumn tableName="order">
<column name="isBlocked" type="boolean">
</addColumn>
</changeSet>
<changeSet author="me" id="2">
<addColumn tableName="order">
<column name="shouldDecline" type="boolean">
</addColumn>
</changeSet>
In DB :
Changelog table:
-------------------------
ID | AUTHOR | AND SO ON |
-------------------------
1 | me | AND SO ON |
2 | me | AND SO ON |
-------------------------
Order Table:
--------------------------------------------
ID | isBlocked | shouldDecline | AND SO ON |
--------------------------------------------
1 | true | false | AND SO ON |
2 | true | false | AND SO ON |
--------------------------------------------
Now I want to keep the changes in Order table, but I want to remove ID#2 from changelog table. So basically I want to keep the changes from the changeSet but I want to delete the entry in changeSet and maintain only ID#1 in change but Order table should have shouldDecline column. Anyway to achieve this?
from documentation there is rollback but it wil also revert the changes from the script which I dont want. Any suggestions on this?
Feel free to remove unnecessary rows from databasechangelog table with a delete SQL statement using change id.
As a result, the DDL stays applied to the database, but the log record of it is deleted.
But you should know, that changelog it's a main feature of liquibase, and if you need to remove records from the changelog, maybe you don't need liquibase at all.
Manually editing the changelog it's convenient for the dev and test environment, but it is strictly not recommended in production.
There is no way to remove a changeset from the databasechangelog table without removing the actual change from the database.
I would question why you want to do this.

How to reference the default schema in liquibase preCondition sqlCheck

Posting here in case it helps someone.
TLDR: ${database.defaultSchemaName} within sqlCheck to access default schema name
I wanted my database changeset to first check if a certain row is present in the database. If not present, it should execute the changeset.
Example:
myschema.customers:
uuid
name
age
0001
Bob
22
0002
Sally
25
What I want: liquibase to check if uuid = 0002 is present in the myschema.customers table. If so, run the changeset, otherwise skip over it.
Initially I tried this:
<changeSet id="00000000000007" author="jhipster">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
select count(*) from customers WHERE uuid= '0002'
</sqlCheck>
</preConditions>
<insert tableName="customers">
<column name="uuid" value="0002"></column>
<column name="name" value="Sally"></column>
<column name="age" value="25"></column>
</insert>
</changeSet>
Bear in mind that I have set the following configuration for liquibase (4.6.1)
spring:
liquibase:
default-schema: myschema
But it seems precondition sqlcheck was checking public.customers and not myschema.customers. This would cause an error when uuid 0002 is already present in myschema.customers.
SOLUTION: use ${database.defaultSchemaName}
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">
select count(*) from ${database.defaultSchemaName}.customers WHERE uuid= '0002'
</sqlCheck>
</preConditions>

Liquibase createIndex column order

I'm trying to use Liquibase for our project. We mainly use Oracle database and some other database less often. I'm trying to figureout how to specify column order in case of indexes. Below is a typical create index change set.
<createIndex indexName="PK_xxxxxxx" tableName="xxxxx" unique="true">
<column name="column_1"/>
<column name="column_2"/>
<column name="column_3"/>
</createIndex>
When it comes to performance and application scalablity, column order in index matters a lot. Can you please guide me if there is a way to specify same while creating index?
PS: As per column tag documentation, attributes afterColumn, position exists and they are applicable only for create table I assume. Here is what documentation says about it.
If used in an 'addColumn' command, this attribute allows you to control where in the table column order the new column goes. Only one of beforeColumn, afterColumn or position are allowed. Since 3.1
Liquibase will use the order of columns as listed in the createIndex tag - very much like the DBMS uses the order specified in the create index statement.
The following changeset:
<changeSet author="arthur" id="1">
<createTable tableName="foo">
<column name="col_1" type="integer"/>
<column name="col_2" type="integer"/>
<column name="col_3" type="integer"/>
</createTable>
<createIndex indexName="ix_one" tableName="foo">
<column name="col_1"/>
<column name="col_2"/>
<column name="col_3"/>
</createIndex>
<createIndex indexName="ix_two" tableName="foo">
<column name="col_3"/>
<column name="col_2"/>
<column name="col_1"/>
</createIndex>
<createIndex indexName="ix_three" tableName="foo">
<column name="col_2"/>
<column name="col_3"/>
<column name="col_1"/>
</createIndex>
</changeSet>
will produce the following statements (when e.g. run with updateSQL):
CREATE TABLE public.foo (col_1 INT, col_2 INT, col_3 INT);
CREATE INDEX ix_one ON public.foo(col_1, col_2, col_3);
CREATE INDEX ix_two ON public.foo(col_3, col_2, col_1);
CREATE INDEX ix_three ON public.foo(col_2, col_3, col_1);

Liquibase Insert computed value with multiple ids

I am trying to insert values into a database. I think the SQL would look like this:
INSERT INTO `tb_config` (`name`, `value`, `description`, `unity_id`)
(SELECT 'new_rule', true, 'rule description', id FROM tb_unity );
However, I want to do it with Liquibase, using a changeset:
<changeSet author="Luis Sukys" id="1022" >
<insert tableName="tb_config">
<column name="name">new_rule</column>
<column name="value">false</column>
<column name="descricao">rule description</column>
<column name="unidade_id" valueComputed="SELECT id FROM tb_unity" />
</insert>
</changeSet>
I've seen the use of valueComputed but with a where clause.
The idea is that it includes one row in tb_config for each id in tb_unity.
I am actually getting a 'ValidationFailedException' from liquibase.
Any help?
I use Navicat with MySQL and when I run this code, it includes one new row int tb_config for each row in tb_unity. The field i use from tb_unity is 'id'.
If I have 05 unities in tb_unity, it must add 05 rows in tb_config, with same values, only change the unity's id.
If you try running that SQL using whatever SQL tool you use, you will see that the problem is that you do indeed need a where clause. Which row of tb_unity do you want to use the id from?