Constraints in liquibase - liquibase

<?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">
<changeSet id="0.1.1.0" author="Bob" failOnError="true">
<createTable tableName="roles">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true"/>
</column>
<column name="name" type="varchar(1024)">
<constraints nullable="false" unique="true"/>
</column>
<column name="fam" type="varchar(1024)">
<constraints nullable="false"/>
</column>
</createTable>
<rollback>
</rollback>
I have such migrations to create a table, now I have changes and need to make other migrations. In the table, the name field is unique, I should get rid of this, that is, the name should not be unique and I need to impose a restriction on the fam. One fam should not be assigned to two roles with the same name. But roles with different names can contain the same fam. thanks in advance

If you want to add changes to the existing schema, you should keep your existing changes the way they are and write new changeSets.
So in order to implement requirement
the name field is unique, I should get rid of this
you could have used <dropUniqueConstraint> change, but it requires a constraintName attribute. But you didn't provide a uniqueConstraintName for the name's unique constraint when you were creating it, which makes implementation tricky. it's always a good practice to give your constraints and indexes names.
So, you may do the following:
create a new column, e.g. non_unique_name
copy all the data from name to non_unique_name
drop name column
rename non_unique_name column to name
The changeSets could look like this:
<changeSet id="0.1.1.1" author="Bob">
<preConditions onFail="MARK_RAN">
<tableExists tableName="roles"/>
<not>
<columnExists tableName="roles" columnName="non_unique_name"/>
</not>
</preConditions>
<comment>create a new column, e.g. non_unique_name</comment>
<addColumn tableName="roles">
<column name="non_unique_name" type="varchar(1024)">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
<changeSet id="0.1.1.2" author="Bob">
<preConditions onFail="MARK_RAN">
<tableExists tableName="roles"/>
<columnExists tableName="roles" columnName="non_unique_name"/>
<columnExists tableName="roles" columnName="name"/>
</preConditions>
<comment>copy all the data from name to non_unique_name</comment>
<update tableName="roles">
<column name="non_unique_name" valueComputed="name"/>
</update>
</changeSet>
<changeSet id="0.1.1.3" author="Bob">
<preConditions onFail="MARK_RAN">
<tableExists tableName="roles"/>
<columnExists tableName="roles" columnName="name"/>
</preConditions>
<comment>drop name column</comment>
<dropColumn tableName="roles" columnName="name"/>
</changeSet>
<changeSet id="0.1.1.4" author="Bob">
<preConditions onFail="MARK_RAN">
<tableExists tableName="roles"/>
<columnExists tableName="roles" columnName="non_unique_name"/>
</preConditions>
<comment>rename non_unique_name column to name</comment>
<renameColumn tableName="roles" oldColumnName="non_unique_name" newColumnName="name"
columnDataType="varchar(1024)"/>
</changeSet>
And in order to implement the requirement:
One dealer should not be assigned to two roles with the same name. But
roles with different names can contain the same dealer.
You may use <addUniqueConstraint> change, with it you can add composite unique constraints. The changeSet could look like this:
<changeSet id="0.1.1.5" author="Bob">
<preConditions onFail="MARK_RAN">
<tableExists tableName="roles"/>
<columnExists tableName="roles" columnName="dealer"/>
<columnExists tableName="roles" columnName="name"/>
</preConditions>
<comment>add unique constraint for roles.dealer and roles.name</comment>
<addUniqueConstraint tableName="roles" columnNames="dealer, name"
constraintName="roles_dealer_name_unique" />
</changeSet>

Related

How to Update a Database table column to constraint nullable='true' | Liquibase

In liquibase, I created a table, one of column is like below
<column name="identifier" type=""varchar2(50)">
<constraint nullable="false"/>
</column>
This changeset is already executed
Now I need to set this column as
<constraint nullable="true"/>
I am using MySql
If you want to change a column to nullable='true' in Liquibase, you can use the modifyColumn tag.
<modifyColumn tableName="table_name">
<column name="identifier" type="varchar2(50)">
<constraint nullable="true"/>
</column>
</modifyColumn>
Note: in Mysql, the equivalent for varchar2 is varchar; So ur modifyColumn tag should look like this:
<modifyColumn tableName="table_name">
<column name="identifier" type="varchar(50)">
<constraint nullable="true"/>
</column>
</modifyColumn>
<modifyDataType
tableName="unspecified"
columnName="identifier"
newDataType="VARCHAR(50) NOT NULL"/>

Liquibase Insert computedvalue with sql multiple row

I want to insert multiple rows to the xxx_table with liquibase.
<changeSet id="XXX" author="XXX">
<comment>Add default values to xxx_table table</comment>
<insert tableName="xxx_table">
<column name="group_id"
valueComputed="(SELECT group_id FROM subscriber)"/>
<column name="business_name"
valueComputed="(SELECT business_name FROM subscriber)"/>
<column name="created_stamp" valueComputed="CURRENT_TIMESTAMP"/>
<column name="last_updated_stamp" valueComputed="CURRENT_TIMESTAMP"/>
</insert>
<rollback/>
</changeSet>
Liquibase script fails because more than one row is returned from '(SELECT business name FROM subscriber)'. How can I insert multiple raw to a table?

Liquibase add a new column and populates the new one with the value of another existing column, operation to be done only if the column does not exist

In the same table I have to migrate the values of a column to a new one;
I want to create a new column,
populate it with the values of the other column and
remove the old column already migrated,
this must only happen if the new column has not already been created.
<changeSet author="xxxx" id="00021">
<preConditions onFail="MARK_RAN">
<not>
<columnExists schemaName="schema" tableName="talbee" columnName="new_table"/>
</not>
</preConditions>
<addColumn schemaName="schema" tableName="new_table">
<column name="new_column" type="text"/>
</addColumn>
<sql>UPDATE table_name SET new_column = old_column</sql>
<dropColumn
catalogName="cat"
columnName="old_column"
schemaName="schema"
tableName="table_name">
<column name="old_column"/>
</dropColumn>
</changeSet>
Is the way I delete the old column correct?
Is it better just to rename the column?
Yes, it's more rational to just rename the existing column. You can use <columnExists> preConditions and then <renameColumn> change.
<changeSet author="xxxx" id="00021">
<preConditions onFail="MARK_RAN">
<tableExists tableName="table_name"/>
<columnExists tableName="table_name" columnName="old_column"/>
<not>
<columnExists tableName="table_name" columnName="new_column"/>
</not>
</preConditions>
<renameColumn tableName="table_name" oldColumnName="old_column" newColumnName="new_column"
columnDataType="text"/>
</changeSet>

Hibernate Envers + Liquibase: NULL not allowed for column "REV"

I want to add Liquibase to an application that already uses Hibernate Envers, but with auto creation of the database tables. Hence, I need to write Liquibase changesets that contain the tables for my entities and the table needed for Envers.
Here's the changeset:
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<property name="long_type" value="bigint" dbms="postgresql"/>
<property name="long_type" value="long" dbms="h2"/>
<changeSet id="CreateBasicSchema" author="Steven Schwenke">
<createSequence sequenceName="hibernate_sequence" startValue="0" incrementBy="1"/>
<createTable tableName="revinfo">
<column name="rev" type="integer">
<constraints primaryKey="true"/>
</column>
<column name="revtstmp" type="bigint"/>
</createTable>
<createTable tableName="stuff">
<column name="id" type="${long_type}">
<constraints nullable="false" unique="true" primaryKey="true"/>
</column>
<column name="text" type="varchar(36)">
<constraints nullable="false"/>
</column>
</createTable>
<createTable tableName="stuff_aud">
<column name="id" type="${long_type}">
<constraints nullable="false" unique="true" primaryKey="true"/>
</column>
<column name="rev" type="integer">
<constraints referencedTableName="revinfo"
foreignKeyName="fk_brands_stuff_revinfo"
referencedColumnNames="rev"
nullable="false"/>
</column>
<column name="revtype" type="integer">
<constraints nullable="false"/>
</column>
<column name="stuff" type="varchar(36)">
<constraints nullable="true"/>
</column>
</createTable>
</changeSet>
This causes the following exception:
o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 23502, SQLState: 23502
o.h.engine.jdbc.spi.SqlExceptionHelper : NULL nicht zulässig für Feld "REV"
NULL not allowed for column "REV"; SQL statement:
/* insert org.hibernate.envers.DefaultRevisionEntity */ insert into revinfo (rev, revtstmp) values (null, ?) [23502-200]
org.springframework.dao.DataIntegrityViolationException: could not execute statement ...
After some research, I finally found my error and will post it as the answer to this question. Hope that this helps other developers encountering the same exception.
The missing part is a simple autoIncrement for the ID of the revinfo table:
<createTable tableName="revinfo">
<column name="rev" type="integer" autoIncrement="true">
<constraints primaryKey="true"/>
</column>
<column name="revtstmp" type="bigint"/>
</createTable>
Without this, new entries in the revinfo table will have a null ID, which is exactly what the exception says.
Another error in the liquibase code above is that the id of the aud-table has a primary key just on id. This will lead to exceptions when updating entities. According to Vlad Mihalcea, there should be a combined key for the aud table:
<createTable tableName="stuff_aud">
<column name="id" type="${long_type}">
<constraints nullable="false" />
</column>
<column name="rev" type="integer">
<constraints referencedTableName="revinfo"
foreignKeyName="fk_brands_stuff_revinfo"
referencedColumnNames="rev"
nullable="false"/>
</column>
<column name="revtype" type="integer">
<constraints nullable="false"/>
</column>
<column name="stuff" type="varchar(36)">
<constraints nullable="false"/>
</column>
</createTable>
<addPrimaryKey tableName="stuff_aud" columnNames="id, rev" />

How add specific type column by liqiubase

Postgresql has type INTERVAL for column. But I didn`t found simple way to create table with such column.
I have solution with two steps:
1. Create table by liquibase changeSet.
2. Use changeSet with sqlFile. Sql file contain sql for add column.
Example changeSet:
<changeSet author="author" id="add-testmy-table-v1">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="testmy"/>
</not>
</preConditions>
<createTable tableName="testmy">
<column autoIncrement="true" name="id" type="BIGSERIAL">
<constraints primaryKey="true" primaryKeyName="PK_testmy"/>
</column>
<column name="created_at" type="TIMESTAMP(6) WITHOUT TIME ZONE"/>
<column name="employee_id" type="BIGINT"/>
</createTable>
</changeSet>
<changeSet author="liquibase-docs" id="sql-example">
<sqlFile dbms="postgresql" encoding="utf8" path="testmy.sql" relativeToChangelogFile="true"/>
</changeSet>
testmy.sql file contain:
ALTER TABLE testmy
ADD COLUMN began_at INTERVAL ,
ADD COLUMN ended_at INTERVAL ;
Could anyone show another variant to solve such problem?