Condition in creating view - liquibase xml - sql

I want to make some view but with condition in my fourth column.
CONDITION:
If epr.j_id is not null then fill lj.id BUT WHEN epr.j_id is null then fill 'NONE'
I've found some condition tag there but I have no idea how I can use it or maybe there is some another way...
<changeSet id="103" author="m">
<createView replaceIfExists="true"
schemaName="public"
viewName="report">
SELECT DISTINCT l.id,
l.name,
l.gis_ext_id AS gis_ext_id_line,
condition (epr.j_id is not null ),

From a previous StackOverflow question: How to add condition to changeSet
If you want to update the columns datatype depending on certain conditions, you can use <preConditions> with <sqlCheck>, and then use <modifyDataType>.
So supposing that there's already a table with name TASK and it contains a column named STATUS, you can modify it's datatype as such:
<changeSet id="1" author="xxx">
<comment>Update Asset table</comment>
<preConditions onFail="MARK_RAN">
<and>
<columnExists tableName="TASK" columnName="STATUS"/>
<sqlCheck expectedResult="0"> SELECT COUNT(*) FROM TASK WHERE STATUS = "pending";</sqlCheck>
</and>
</preConditions>
<modifyDataType tableName="TASK" columnName="STATUS" newDataType="VARCHAR(10)"/>
</changeSet>
Or perhaps you'd like another onFail-behaviour. Check out this link
HALT - Immediately halt execution of entire change log [default]
CONTINUE - Skip over change set. Execution of change set will be attempted again on the next update. Continue with change log.
MARK_RAN - Skip over change set, but mark it as ran. Continue with change log
WARN - Output warning and continue executing change set as normal.
Additional Documentation:
Liquibase: Create View
Mohit Goyal: Check database state and conditionally apply changes in the Liquibase

Related

Add new column via liquibase only if not already exists

I want to use liquibase on spring boot startup to add a new column to an existing table.
But in the particular schema I'm working with I have already added it manually.
So I get an error on startup indicating the column already exists (because it does):
Error creating bean with name 'liquibase' defined in class path resource
[...LiquibaseConfiguration.class]: LiquibaseException:
liquibase.exception.MigrationFailedException: Migration failed for
change set db/changelog/AlterTable_MyTable_AddNewCol.xml::1::me (manual):
Reason: liquibase.exception.DatabaseException:
Duplicate column name 'new_col' [Failed SQL: (1060)
ALTER TABLE my_schema.my_table ADD new_col INT NULL]
Is there a liquibase precondition option to add the column only if it does not already exist; and if it already exists, don't error out?
I tried the following and the process errors out with the above error:
<databaseChangeLog ... >
<changeSet author="me (manual)" id="1">
<preConditions onFail="WARN">
<columnExists tableName="my_table" columnName="new_col" />
</preConditions>
<addColumn tableName="my_table">
<column name="new_col" type="integer"/>
</addColumn>
</changeSet>
</databaseChangeLog>

How to use the result of a query as liquibase defaultValueComputed?

Working with liquibase I've found that it's possible to define properties, and use them as defaultValue for columns.
Everything is ok if you use pre-made functions, but how to do when you have some custom SQL that produces some data, to be used in the same manner?
I tried to do something like this:
<property name="myFunc" value="SELECT CURRENT_DATE + time '08:00:00' + (random() * interval '3 hours')::time" dbms="postgresql"/>
...
<column name="selected_pickup_date" type="datetime" defaultValueComputed="${myFunc}" remarks="Selected from customer during order creation">
<constraints nullable="false"/>
</column>
But it didn't work! The error code showed that liquibase was just placing my SQL string as the column's default value.
SQL wasn't parsed as I expected.
How to proceed to get the query result used as default value?
After many attempts, I understood that Liquibase doesn't parse SQL in properties, probably it only executes functions and places their result in the property value, and the placeholder ${xxx} is replaced with the function's output.
Once a new row is added to the table, and the column with defaultValue is empty, the property value is read and used in place of NULL.
I tried many solutions and luckily the last one worked:
Take the SQL and create a new function in a sql file, in my case it was simple:
DROP FUNCTION IF EXISTS fixed_date_rand_time;
CREATE FUNCTION fixed_date_rand_time() RETURNS timestamp
AS $$ SELECT CURRENT_DATE + time '08:00:00' + (random() * interval '3 hours')::time $$
LANGUAGE SQL;
Import the sql before other changelogs
<include relativeToChangelogFile="true" file="func.sql" />
Now..you can create the property! This time calling the function instead of the SQL string
<property name="myFunc" value="fixed_date_rand_time()" dbms="postgresql"/>
...
<changeSet id="20201026174848-1" author="jhipster">
<createTable tableName="my_table">
<column name="selected_pickup_date" type="datetime" defaultValueComputed="${myFunc}" remarks="Selected from customer during order creation">
<constraints nullable="true"/>
</column>
</createTable>
</changeSet>
...
Et Voilà! It works now!
The very-nice thing is that properties can be used with context too, allowing you to bypass the new function when outside of local test environment:
<property name="myFunc" value="fixed_date_rand_time()" dbms="postgresql" context="test"/>
<property name="myFunc" value="NULL" dbms="postgresql" context="!test"/>
Not perfect, it works only with nullable columns, but there must a solution.
Finally the database is loaded with correct values automatically, taking away the hassle of updating dates everyday, to have everything work as expected.
I hope that this solution can help somebody, who maybe has the will to refine it a bit :-)
My Liquibase version is 3.10, I cannot update to the latest due to other dependencies... maybe the problem has been addressed there.
I don't exclude that the feature has been disabled intentionally in favour of the pro version.
Have a nice day

Liquibase changelog parameters in liquibase.properties

As per documentation parameter values are looked up in the following order:
Passed as a parameter to your Liquibase runner (see Ant, command_line, etc. documentation for how to pass them)
As a JVM system property
In the parameters block ( Tag) of the DatabaseChangeLog file itself.
Can I set these parameters in the standard properties file?
It is possible to put changelog parameters in liquibase.properties, or a custom --defaultsFile. Reading the source indicates that you must prefix the properties with "parameter.".
Your error probably looks something like this:
SEVERE 11/4/16 10:26 AM: liquibase: Unknown parameter: 'read_only_user'
liquibase.exception.CommandLineParsingException: Unknown parameter: 'read_only_user'
at liquibase.integration.commandline.Main.parsePropertiesFile(Main.java:453)
Example liquibase.properties:
driver=oracle.jdbc.driver.OracleDriver
url="jdbc:oracle:thin:#(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=dbhost)(PORT=1600))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=devdb)))"
username=scott
password=tiger
defaultSchemaName=app_admin
promptOnNonLocalDatabase=false
parameter.read_only_user=app_read
Using the parameter in a changeset:
<changeset author="Ryan" id="1">
<sql>GRANT SELECT ON APP.SOME_TABLE TO ${read_only_user}</sql>
</changeset>
Yes, and is very usefull if you want to manage information under control...like passwords in differents enviroments:
liquibase.properties:
parameter.pass_admin=idonthavepassword
and the changelog:
<changeSet author="rbs" id="create_user_admin" labels="inicial">
<preConditions onFail="CONTINUE">
<sqlCheck expectedResult="0">SELECT COUNT(1) FROM pg_roles WHERE rolname='admin'</sqlCheck>
</preConditions>
<sql>CREATE USER admin PASSWORD '${pass_admin}' LOGIN SUPERUSER NOCREATEDB NOCREATEROLE INHERIT NOREPLICATION CONNECTION LIMIT -1
<comment>Creating admin user</comment>
</sql>
</changeSet>
Yes, it is possible: http://www.liquibase.org/documentation/liquibase.properties.html.
And use --defaultsFile to pass property file.

Is it possible to classify Liquibase changesets

I would like to classify Liquibase changesets like
"must run" (e.g. add column)
"can run" (e.g. change column size)
Is there a way to do something like that?
The reason to do this is that the execution of changesets should not stop if a changeset from class "can run" runs in a ValidationFailedException or something similar.
Thanks
You can mark your changesets with an attribute
failOnError
Example:
<changeSet id="changeset1" failOnError="true">
<!-- do important stuff here -->
</changeSet>
<changeSet id="changeset2" failOnError="false">
<!-- do not so important stuff here -->
</changeSet>

Liquibase: changeLogPropertyDefined doesn't work

I'm trying to execute one of two insert depending on value of Java system property 'type'. But when I run updateSQL, it generates both insert entries despite of value and even presence of variable 'type'. This are my change sets:
<!--changes for CS only-->
<changeSet id="57" author="mborodin">
<preConditions onFail="CONTINUE">
<changeLogPropertyDefined property="type" value="cs"/>
</preConditions>
<sql>INSERT INTO cs.config(key, value) values('autochecks.findProfilesInDb', 'true'),
('web.type', 'cs'), ('globalFailureDetectorEnabled', 'true');
</sql>
</changeSet>
<!--changes for LCS only-->
<changeSet id="58" author="mborodin">
<preConditions onFail="CONTINUE">
<and>
<changeLogPropertyDefined property="type" value="lcs"/>
<not>
<changeSetExecuted
id="57"
author="mborodin"
changeLogFile="${changeLogFile}"
/>
</not>
</and>
</preConditions>
<sql>INSERT INTO cs.config(key, value) values('autochecks.findProfilesInDb', 'false'),
('web.type', 'local-cs'), ('globalFailureDetectorEnabled', 'false');
</sql>
</changeSet>
What's wrong? Is there a better way to work it?
From the first nvoxland's answer at liquibase forum:
… we added the onSqlOutput tag. IGNORE just assumes that the
precondition cannot be applied, but you should continue to "run" the
changeLog. TEST means to actually run the precondition. FAIL mean to
stop the execution of the changelog, alerting you that you cannot run
in updateSql mode because a correct execution of the precondtion is
required to continue.
The problem has been solved by adding onSqlOutput="TEST" tag.