Liquibase changeset changeLogPropertyDefined is not working - liquibase

In liquibase I want to execute a particular change set based on the 'context' property value.
In this case I have passed -Dcontext=local (I've checked this values is getting picked properly) through command line and tried to check that property within my changeset by using changeLogPropertyDefined. But It's not working..
Please find below my changeset
<changeSet id="1" author="dm">
<preConditions onFail="MARK_RAN" onSqlOutput="TEST">
<changeLogPropertyDefined property="context" value="local"/>
</preConditions>
<createTable tableName="accountold">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="version" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>

You should add the "link" to the property in the top of your changeset file to reference value is passed from jvm args:
<property name="context" value="${context}"/>
Then you could use your property in precondition and anywhere you want in this changeset.
P.S. Maybe it's will be better to give different name for jvm arg and lb property to avoid ambiguity of interpretation.

Related

How to migrate join table to entity table with liquibase without liquibase.exception.DatabaseException: ERROR: column "id" contains null values

I'm making the upgrade of the table that previously was just a join table to the real entity. So, it should have no primary key of ids of the entities it joins, and establish its own id. I tried this update configuration:
<changeSet id="20200429180824-1" author="jhipster">
<dropPrimaryKey columnNames="teacher_id, subject_id" tableName="teacher_subject"/>
<addColumn tableName="teacher_subject" author="jhipster">
<column name="id" type="bigint" autoIncrement="${autoIncrement}">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="teacher_status" type="varchar(255)" defaultValue="APPROVED">
<constraints nullable="true" />
</column>
<column name="achieved" type="datetime" defaultValueComputed="CURRENT_TIMESTAMP" >
<constraints nullable="true" />
</column>
<column name="active" type="boolean" defaultValue="true">
<constraints nullable="true" />
</column>
<!-- jhipster-needle-liquibase-add-column - JHipster will add columns here, do not remove-->
</addColumn>
<dropDefaultValue tableName="teacher_subject" columnName="achieved" columnDataType="datetime"/>
</changeSet>
However, at the migration time I'm getting failure with this exception, despite of my expectation that the column should be just created at the moment and pre-populated with autoIncrement data:
liquibase.exception.DatabaseException: ERROR: column "id" contains null values
However, I have realized the root of the problem here is in forcing "nullable=false". After moving it to the different and later execution of the same changeset, the migration succeed. The final changeset looks like, note separate addNotNullConstraint and primaryKey-only <constraints primaryKey="true"/> here:
<changeSet id="20200429180824-1" author="jhipster">
<dropPrimaryKey columnNames="teacher_id, subject_id" tableName="teacher_subject"/>
<addColumn tableName="teacher_subject" author="jhipster">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true"/>
</column>
<column name="teacher_status" type="varchar(255)" defaultValue="APPROVED">
<constraints nullable="true" />
</column>
<column name="achieved" type="datetime" defaultValueComputed="CURRENT_TIMESTAMP" >
<constraints nullable="true" />
</column>
<column name="active" type="boolean" defaultValue="true">
<constraints nullable="true" />
</column>
<!-- jhipster-needle-liquibase-add-column - JHipster will add columns here, do not remove-->
</addColumn>
<addNotNullConstraint catalogName="cat"
columnDataType="bigint"
columnName="id"
constraintName="teacher_subject_id_not_null"
tableName="teacher_subject"
validate="true"/>
<dropDefaultValue tableName="teacher_subject" columnName="achieved" columnDataType="datetime"/>
</changeSet>

Liquibase loadData as string, not CLOB resource

The Problem
I recently upgraded Liquibase to 3.6.2 from 3.4.2.
Loading seed data from a CSV into text fields now results in a CLOB resource error. Before it would simply insert the text as a value.
The Setup
I'm using Liquibase to manage migrations of my data.
I have a table with an code and description column. description is of type TEXT.
<changeSet author="" id="create-table-degrees">
<createTable tableName="degrees">
<column name="code"
type="varchar(2)">
<constraints primaryKey="true"/>
</column>
<column name="description"
type="text">
<constraints unique="true"/>
</column>
</createTable>
<rollback>
<dropTable tableName="degrees"/>
</rollback>
</changeSet>
I have seed data in a CSV:
code,description
"D1","MASTERS"
"D2","DOCTORATE"
I load it using loadData:
<changeSet author="" id="seed-degrees">
<loadData file="seeds/degrees.csv"
tableName="degrees" />
</changeSet>
The Error
Unexpected error running Liquibase: CLOB resource not found: MASTERS
The Question
Is there a way to keep Liquibase from interpreting seed values as file paths instead of strings, or do I need to manually define the column types as String in loadData.
e.g. I would like to avoid having to modify the old changeSet to:
<changeSet author="" id="seed-degrees">
<loadData file="seeds/degrees.csv"
tableName="roles">
<column name="description" type="string" />
</loadData>
</changeSet>
The workaround listed in CORE-3287: Anver S December 3, 2018, 3:07 PM
While adding an explicit column type definition as defined in original
stackoverflow post
<column name="description" type="string" />
does the trick - for me it effectively requires to update already
applied changesets which ideally I'd try to avoid.

Liquibase - insert rows with uuid

I have two tables declared as follows:
<changeSet author="istvan" id="country-table-changelog">
<createTable tableName="country">
<column name="id" type="uuid">
<constraints nullable="false" unique="true" />
</column>
<column name="name" type="varchar">
<constraints nullable="false" unique="true" />
</column>
</createTable>
</changeSet>
<changeSet author="istvan" id="region-table-changelog">
<createTable tableName="region">
<column name="id" type="uuid" >
<constraints nullable="false" unique="true" />
</column>
<column name="country_id" type="uuid">
<constraints nullable="false" />
</column>
<column name="name" type="varchar">
<constraints nullable="false" unique="true" />
</column>
</createTable>
</changeSet>
<changeSet author="istvan" id="region-country-foreign-key-constraint">
<addForeignKeyConstraint
baseTableName="region"
baseColumnNames="country_id"
referencedTableName="country"
referencedColumnNames="id"
constraintName="fk_region_country"
onDelete="CASCADE"
onUpdate="RESTRICT"/>
</changeSet>
I want to fill both tables from liquibase changelog file with some values like:
INSERT INTO country VALUES('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'HUNGARY');
INSERT INTO region VALUES('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'Baranya');
In the example I used aaaa's and bbbb's just because of simplicity. I want to generate those UUID's by the DBMS.
What is the best way to do it? Do I have to use SQL in my changelog files or is it possible with XML? I prefer DBMS independent solution like XML or JSON.
My second question is that how can I declare a column with UUID that creates the UUID on insert. Something like:
<column name="id" type="uuid" value="??? GENERATE UUID ???">
<constraints nullable="false" unique="true" />
</column>
Thank you for your time!
You can do this by using properties that are defined depending on the current DBMS.
<property name="uuid_type" value="uuid" dbms="postgresql"/>
<property name="uuid_type" value="uniqueidentifier" dbms="mssql"/>
<property name="uuid_type" value="RAW(16)" dbms="oracle"/>
<property name="uuid_function" value="uid.uuid_generate_v4()" dbms="postgresql"/>
<property name="uuid_function" value="NEWID()" dbms="mssql"/>
<property name="uuid_function" value="sys_guid()" dbms="oracle"/>
Then use those properties when defining the table:
<column name="id" type="${uuid_type}" defaultValueComputed="${uuid_function}">
<constraints nullable="false" unique="true" />
</column>
Note that you need to use defaultValueComputed, not value
If the column is defined with a default value, just leave it out in your insert statements and the database will then generate the UUID when inserting.
For MySQL, put your property just before changeSet tag:
<property name="u_id" value="uuid()" dbms="mysql"/>
then
<column name="ID" type="varchar(255)" valueComputed="${u_id}"/>
NOTE: here valueComputed is used, not defaultValueComputed.
Some databases supports UUID columns: Generate UUID values by default for each row on column of UUID type in H2 Database Engine
I don't think that Liquibase has embedded UUID generator, have a look at defaultValueComputed/valueComputed property for column (http://www.liquibase.org/documentation/column.html) + DB function to generate UUID
Like Rammgarot noted, since we are dealing with columns that need to have unique values, we should use valueComputed instead of defaultValueComputed.
Any of the provided answers didn't help me in case of MySQL, but I was able to work it out using another approach - using triggers.
<changeSet author="nberezovski" id="1">
<createTable tableName="test">
<column name="id" type="varchar(255)">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="description" type="varchar(255)">
...
</createTable>
</changeSet>
<changeSet author="nberezovski" id="2">
<sql splitStatements="false">
CREATE TRIGGER insert_test
BEFORE INSERT ON test
FOR EACH ROW
BEGIN
IF (NEW.id IS NULL) THEN
SET NEW.id = UUID();
END IF;
END;
</sql>
</changeSet>
After that, each manual insert only of values for description MySQL automatically generated an id for me. For example:
insert into test(description) values('some description');
Also that approach helped in loading data using loadData
For SQL SERVER works
<column name="uuid" valueComputed="newid()" />
For me helped this (postgres 14.2):
<property name="uuid_function" value="gen_random_uuid()" dbms="postgresql"/>
<column name="id" valueComputed="${uuid_function}"/>

jhipster - issue with custom relationship between two entities

I m trying to create a relationship between two entities.
Entity Fournisseur(id,code,libelle)
Entity Catalogue (id, fournisseur_code)
I want the relationship between those two entities be between code and fournisseur_code.
I have modified the liquibase xml generated file for entity catalogue from
<changeSet id="20150116113044" author="jhipster">
<createTable tableName="T_CATALOGUE">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="fournisseur_id" type="bigint"/>
<column name="produit_id" type="bigint"/>
<column name="marque_id" type="bigint"/>
<column name="pays_id" type="bigint"/>
<column name="emballage_id" type="bigint"/>
</createTable>
<addForeignKeyConstraint baseColumnNames="fournisseur_code"
baseTableName="T_CATALOGUE"
constraintName="fk_catalogue_fournisseur_id"
referencedColumnNames="id"
referencedTableName="T_FOURNISSEUR"/>
to
<changeSet id="20150116113044" author="jhipster">
<createTable tableName="T_CATALOGUE">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="fournisseur_code" type="varchar(45)"/>
<column name="produit_id" type="bigint"/>
<column name="marque_id" type="bigint"/>
<column name="pays_id" type="bigint"/>
<column name="emballage_id" type="bigint"/>
</createTable>
<addForeignKeyConstraint baseColumnNames="fournisseur_code"
baseTableName="T_CATALOGUE"
constraintName="fk_catalogue_fournisseur_code"
referencedColumnNames="code"
referencedTableName="T_FOURNISSEUR"/>
The table was well generated, but when I m trying to run the getAll function from CatalogueResource, it tells me :
[ERROR] org.hibernate.engine.jdbc.spi.SqlExceptionHelper - Unknown
column 'catalogue0_.fournisseur_id' in 'field list'
I can't figure out why.
If someone knows....
Thank you.
the relation must be connected with the field.
you have to change:
baseColumnNames="fournisseur_code"
to this
baseColumnNames="fournisseur_id"

Liquibase: How to set the default value of a date column to be "now" in UTC format?

How do you set the default value of a date column to be "now" in UTC format? I think the answer involves the defaultValueComputed attribute on the column element.
The documentation states:
defaultValueComputed A value that is returned from a function or
procedure call. This attribute will contain the function to call.
What langauge is the function referred to supposed to be written in? Java? Is the function supposed to be the database vendor -specific date function I want to use? Is there any more documentation I can read on this topic?
Maybe this topic in the liquibase forum will help?
I think defaultValueComputed will take a database specific function to express "now". In mySQL it would be CURRENT_TIMESTAMP so it could look like this:
<createTable tableName="D_UserSession">
<column name="ts" type="TIMESTAMP" defaultValueComputed="CURRENT_TIMESTAMP"/>
</createTable>
(Copied from the forum post.)
This should be:
<property name="now" value="now()" dbms="mysql,h2"/>
<property name="now" value="current_timestamp" dbms="postgresql"/>
<property name="now" value="sysdate" dbms="oracle"/>
<property name="now" value="getdate()" dbms="mssql"/>
<changeSet author="me" id="sample_usage_demo">
<addColumn schemaName= "dbo" tableName="demo_table" >
<column name="demo_column" type="datetime" defaultValueDate="${now}">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
In MySQL, to use a DATETIME column with fractions of second like DATETIME(6) (microseconds precision), use default value of NOW(6) (caution: CURRENT_TIMESTAMP(6) for some reason produces an error with me using liquibase 3.5.3):
<column name="created_at" type="DATETIME(6)" defaultValueComputed="NOW(6)" >
<constraints nullable="false" />
</column>
Note that the value will be stored internally in UTC, but read using the server's timezone settings (##global.time_zone, ##session.time_zone).
This works with SQlite:
<column name="last_updated_at" type="TIMESTAMP" defaultValueComputed="CURRENT_TIMESTAMP">
<constraints nullable="false"/>
</column>
Adding '$now' didn't work for me. I am using SQlite as the DB.
This worked for me:
<property name="now" value="UNIX_TIMESTAMP()" dbms="mysql"/>
<column name="ts" type="timestamp" valueDate="${now}"/>
I found it thanks to this answer: https://stackoverflow.com/a/9100388/3107952
You should probably use timestamp with time zone as that will keep the timestamps in UTC as opposed to local server time, which might be problem if you have a multi-region setup.
You can readmore about the timezone part on this Stack Overflow post.
databaseChangeLog:
- changeSet:
id: 007
author: joe
changes:
- addColumn:
tableName: my_table
columns:
- column:
name: created_at
type: timestamp with time zone
- addDefaultValue:
columnName: created_at
defaultValueComputed: now()
tableName: my_table
Due to the fact, that the requested timezone UTC is not mentioned in all of the answers here, I'd like to state another set of solutions for different database vendors.
Especially, the current answers here does not state the correct solution for Oracle.
<changeSet logicalFilePath="my_changeset.xml"
id="1"
author="me"
dbms="mariadb,h2">
<addColumn tableName="MY_TABLE">
<column name="MY_ZONED_DATE_TIME_COLUMN"
type="timestamp(6)"
defaultValueComputed="now()">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
<changeSet logicalFilePath="my_changeset.xml"
id="1"
author="me"
dbms="postgresql">
<addColumn tableName="MY_TABLE">
<column name="MY_ZONED_DATE_TIME_COLUMN"
type="timestamp(6)"
defaultValueComputed="timezone('UTC', now())">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
<changeSet logicalFilePath="my_changeset.xml"
id="1"
author="me"
dbms="oracle">
<addColumn tableName="MY_TABLE">
<column name="MY_ZONED_DATE_TIME_COLUMN"
type="timestamp(6)"
defaultValueComputed="sys_extract_utc(systimestamp)">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
(One may use properties, like showed in Dominika's answer, but we had some really bad experiences with properties in liquibase.)
Summary:
now() is fine for MariaDB, MySql and H2v
now() is not completely fine for H2, I just got a correct result starting the h2 database with UTC like jdbc:h2:mem:./my_database;TIME ZONE=UTC (h2database in version 2.x needed). Then now() is working for sure.
I'm not sure about current_timestamp on PostgreSQL, but timezone('UTC', now()) works. :)
In Oracle, sysdate (mentioned in a few other answers here) is not enough, see as well "How to get UTC value for SYSDATE on Oracle", but sys_extract_utc(systimestamp) does the trick.
I used function the database vendor.
For Oracle it`s a sysdate:
<column name="create_date" type="DATETIME" valueDate="sysdate" defaultValueComputed="sysdate" />
As liquibase is common changelog for any database, to make it generic you should not depend on any specific database like oracle, postegres, mysql instead it should be generic enough to work for any/every database.
Below is how it should be implemented :
<column name="time" type="${type.datetime}" defaultValueComputed="${column.datetime.defaultValue}"/>
This should work for all databases, for oracle, it inserts SYSTIMESTAMP as DATA_DEFAULT.