How do I differentiate between databases when using e.g. sequence - liquibase

I just started using Liquibase and stumpled upon the problem to differentiate between the capabilities of different databases.
We would like to support multiple databases (Oracle, MySQL, Derby - to name three).
The all have different capabilities. In specific Oracle supports sequences whereas MySQL and Derby do not.
When I let hibernate generate the DDL I can choose different dialects and it will consider these different capabilities and generate a Sequencer when using Oracle and use a plain table (for ID-generation) when using Derby or MySQL.
Now, I know I can constraint changesets by specifying 'oracle' in the dbms attribute. But then how can I do the plain table solution for the other databases? There does not seem to be a 'not oracle' attribute for dbms.
How does anyone else handle this? (I could not find anything about it on the liquibase pages nor on the forum.)

Try using a precondition on the changset. Boolean operations are supported.
For example
<preConditions onFail="CONTINUE">
<or>
<dbms type="oracle" />
<dbms type="mysql" />
</or>
</preConditions>

An alternative approach is to put all your sequences in a changlog file which you include in your main changelog, and then do something like this:
<changeSet
dbms="oracle,db2,db2i"
author="mccallim (generated)"
id="1419011907193-1"
>
<createSequence
schemaName="${main.schema}"
...
That changeset only gets executed for the DBMSs listed.

Related

List of supported dbms values

In Liquibase I can set properties based on what DBMS I'm using, as mentioned here: Liquibase changeset by dbms type
For example:
<property name="val" dbms="postgresql" value="x"/>
<property name="val" dbms="h2" value="y"/>
My question is - where can I find a list of all valid/possible dbms values? In the Liquibase docs it just points to a page saying what databases are supported, but it does not give a corresponding dbms value. I know MYSQL is 'mysql', oracle is 'oracle', and so on, but where is the canonical list of values?
I searched the github repo for liquibase core, but can't find the magic class or enum that defines all these values.
Does anyone know where they are?
I don't think there is a canonical list of what values are accepted. From what I can tell, it takes the value you provide for dbms and compares it to the short name for the connected database, so it isn't a list that it is being compared to.
If you are not connected to the database, you can usually find the dbms short name via the connection url or by looking for the getShortName() function in the database files. For example, to connect to AWS redshift, the url is jdbc:redshift://endpoint:port/database and the dbms value you'd set is just redshift. This can also be confirmed by looking at the Redshift extension for Liquibase. The function getShortName() returns redshift
If you are connected to the database, you can easily find out what the value is by running liquibase status and have a precondition with dbms set to some value in a changelog. For example (in XML),
<preConditions>
<dbms type="mongo"/>
</preConditions>
results in Unexpected error running Liquibase: Validation Failed: 1 preconditions failed changelog_mongo.xml : DBMS Precondition failed: expected mongo, got mongodb
The expected value is the value you provide for dbms, and the got value is the connected database short name.
But to have a "list", I looked around and found the following ones listed at some point:
cockroachdb, db2, derby, edb, firebird, h2, hsqldb, informix, ingres, mariadb, mock, mssql, mysql, postgresql, sqlite, sybase

Liquibase Single Precondition for multiple Changesets

I have a number of changesets that I would like to run if a specific condition exists. For example run changesets 1, 2, and 3 only if sqlCheck executed with the excepted results.
I can copy the precondition into each changeset. However it feels like there should be a more efficient way of doing this. As the number of changesets grows, the files have a lot of these duplicates.
The preConditions element directly under databaseChangeLog seems to only configure dbms and runAs.
Is there a way to define a single preCondition that will be used by multiple change sets?
Any help is appreciated.
Unfortunately this is not possible but sometimes you can avoid using preconditions when you declare a property tag instead. For example when you have preconditions for different databases like Oracle and SQL Server which have different data types like
number in Oracle and float in SQL Server, you can instead of using a precondition for each database use a propertytag:
<property dbms="oracle" name="DECIMALTYPE" value="NUMBER" />
<property dbms="mssql" name="DECIMALTYPE" value="FLOAT" />

setColumnRemarks is not supported on mysql

I use liquibase diff generate the diff between dev mysql and prod mysql, I see there exist some change set like:
When I run the diff file, it shows exception like:
setColumnRemarks is not supported on mysql, db/changelog/V1.1.0_prod_uat.xml::1524893614482-984::davy (generated)
setColumnRemarks is not supported on mysql, db/changelog/V1.1.0_prod_uat.xml::1524893614482-985::davy (generated)
setColumnRemarks is not supported on mysql, db/changelog/V1.1.0_prod_uat.xml::1524893614482-986::davy (generated)
at liquibase.changelog.DatabaseChangeLog.validate(DatabaseChangeLog.java:276)
at liquibase.Liquibase.update(Liquibase.java:198)
at liquibase.Liquibase.update(Liquibase.java:179)
at liquibase.integration.spring.SpringLiquibase.performUpdate(SpringLiquibase.java:317)
at liquibase.integration.spring.SpringLiquibase.afterPropertiesSet(SpringLiquibase.java:269)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1769)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1706)
How to resolve this problem?
Object's comment update (Liquibase's set*Remarks) is not supported for some databases, including MySQL. So immediate fix could be to remove this action from your change log.
In my project we generating change sets by comparing (with liquibase diffChangeLog) 2 schemas, and setColumnRemarks actions are included although they cannot be executed afterwards. So I'm just removing these actions from resulting change sets.
I am afraid a proper solution would be to add appropriate MySQL SQL generator implementation to Liquibase.

How can I rename a sequence with liquibase?

I have a sequence in my database, which I generated through Liquibase. During refactoring, we decided that we didn't like the name that we gave it, and we would like to rename it, preserving all data that currently exists for it.
It seems possible to alter a sequence, but I'm not seeing anything about how to rename the sequence. Is there a way to do it, or a reasonable workaround?
(If it matters, I'm using Oracle SQL)
Although not documented, this refactoring is supported by Liquibase. Not sure what version this change was implemented in, but the class supporting the feature was commited on 30 Jan 2014. What's interesting though, is that the original issue is still unresolved.
Anyway, the refactoring is supposed to be working only on Oracle and Postgres. I've tested it on Oracle with Liquibase 3.4.1:
databaseChangeLog:
- changeSet:
id: change_set_id
author: me
dbms: oracle
changes:
- renameSequence:
oldSequenceName: old_name_seq
newSequenceName: new_name_seq
The above refactoring is in YAML format, but you could easily guess its XML counterpart.
On Oracle, this generates the following statement:
RENAME old_name_seq TO new_name_seq;
2 other supported parameters are catalogName and schemaName.
There is not currently a built-in refactoring to rename a sequence. If your database engine supports it, you could execute whatever methods are supported using a <sql> or <sqlFile> change.
You said you were using Oracle SQL. The RENAME statement allows for renaming a sequence. So your Liquibase script would look like this:
<sql>RENAME old_sequence_name TO new_sequence_name</sql>

liquibase subselect - how does one build the XML format?

I would like to use a subselect in a in Liquibase. Does Liquibase support SubSelects other than embedded SQL (i.e. the sql tag) ? If so, can you point me to how to build something like a please?
I assume you mean doing a subselect in an insert or update or create tag.
You cannot use sub-selects in the createTable but you can use the valueComputed tags in and . For example:
<update tableName="person">
<column name="employees" valueComputed="(select count(*) from person where manager=person.id)"/>
</update>
NOTE: If you don't want to drop all the way down to a <sql> tag for commands not handled by the standard XML tags (like subselects in createTable) You can also use to introduce changes and additions to what liquibase can generate from the XML.