Liquibase Single Precondition for multiple Changesets - liquibase

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" />

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 update values inserting single quotes

I'm trying to upgrade Liquibase from 3.3.1 to 3.10.3. Existing changesets are failing because varchar values being udpated are now inserting additional single quotes. Given the following statement in a changeset:
<update tableName="foo_table">
<column name="foo_id" type="VARCHAR(32)" value="'a'"/>
<where>bar_id REGEXP 'A'</where>
</update>
Liquibase seems to be adding additional single quotes in 3.10.3 so that values in the database are now stored with single quotes. Removing the single quotes from value in the changeset corrects the issue but will change the checksum on many of our changesets. I was wondering if there were any options available that will avoid us from needing to do that.
One option would be to create a new changelog (you can keep your old one, refer to both in order using includeAll in a master changelog). In the new changelog, you can just create your changes in sql.
This would allow you to write the change as sql exactly the way you want it, and liquibase would not have to render the sql based on xml.

Determine "version" of DB from Liquibase tables

We use Liquibase for configuration management across multiple MS SQL Server DB instances. If all DBs have executed all available Liquibase scripts, is the following a reliable query to confirm that all DBs are in sync? I am looking for a way to do this from a DB script and not Maven or any other command line utility.
select top 1 ID from DATABASECHANGELOG
order by DATEEXECUTED desc
That might work for really simple scenarios, but if your changelogs have any kind of 'conditional' parts you are going to need some more logic than that.
What you really want to know is whether the set of changes applied to each database is the same. Since changesets are identified by id+author, you should get both those columns from DATABASECHANGELOG and then do set comparison on those to see that the sets were exactly equal.
I would suggest tagging your database. Two supported mechanisms:
tagDatabase refactor command, contained in a changeset
command line tag option
I personally would favor the first option so that the versioning is built into the changeset files. The second option is useful when performed as part of your application's upgrade process (Create a rollback marker).
Finally, once your database is tagged the latest version can be retrieved using SQL:
SELECT TOP 1 cl.tag
FROM DATABASECHANGELOG cl
WHERE cl.tag is not null
ORDER BY 1 DESC
Obviously this approach assumes that your tag strings have a numeric component, so that they sort as expected.
Besides the conditional changeSets that #SteveDonnie mentioned, the order that changeSets execute in can vary depending on how and when you are updating your changelog and database. For example, if you have changeSets A,B and then developer X adds C and developer Y updates D then X's order is A,B,C and Y's order is A,B,D. When they both merge their changes together the final order may be A,B,C,D and when X runs the final version his order will be A,B,C,D but Y's final order will be A,B,D,C.
Y's database will be fully up to date, but a SELECT TOP 1 will return "C" for him even though you were expecting "D". It may or may not be a scenario you run into, but is another reason why a simple single row select will not tell you if databases are up to date or not.
Liquibase does have a "status" command you can run against a database and a changelog and it will return whether the database is up to date or not.

Liquibase : Migrate.sql does not check precondtions

I use liquibase to generate SQL files based on the changeset . But when I am using liquibase:updateSQL , it is not checking for preconditions before generating the SQL file
I have a preconditions saying to check if the table exists , before creating the table .
In this scenario even though the tables are present the 'create table SQL statement is still getting generated.'
Where as when i run liquibase:update goal this precondition is checked properly .
So does not liquibase check for precondition while generating the sql file?
Thanks.
You probably have found an answer already, but just in case this might help:
http://forum.liquibase.org/topic/unexpected-behaviour-of-preconditions-with-updatesql
onSqlOutput="TEST" attribute for the precondition sounds like what you are looking for.
Just be careful with the the preconditions you write or when to test them while generating the updateSQL. Preconditions that rely on previously applied patches that might have not been executed yet in your instance could cause errors.
Hope it helps
Might help to provide some code. Especially your precondition to allow other to evaluate if it is an issue.

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

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.