Bad changelog from generateChangeLog with MariaDB - liquibase

I am attempting to generate a change log against a MariaDB server. I am able to successfully generate a change log, however if I do a dropAll and update to validate that it is useful, there are multiple problems with it. I have tried using both the native mariadb and MySQL jdbc connectors and both experience these problems. I am also using liquibase 3.1.1.
The first is that there are deferrable and initiallyDeferred flags which are not supported by MySQL. The update command specifically calls these out as being invalid flags against MySQL.
Once I remove all of those references in the .xml, attempting to update runs into a sql syntax error because a double datatype is defined as DOUBLE(22) (in the xml). This is not a valid syntax for a double in mariadb or MySQL. They accept no parameters, or DOUBLE(m,d); my database is defined as default (no parameters).
Now its trying to create a table with an auto_increment but not specifying that the column is also a key in the create table statement; ie. it's missing the primaryKey constraint.
And I'm sure there are more problems in line as I work my way through the changelog (this is just changeset 116 out of 1500+).
Its almost as if liquibase is creating the changelog based on it thinking the db is a different type (postgres/oracle?).
Am I missing something?

You are right, the problem is that Liquibase is thinking it is an unknown database and doesn't know it is almost mysql. There is an issue open to add mariaDB support (https://liquibase.jira.com/browse/CORE-1411) but it hasn't been implemented quite yet.
The easiest work-around would be to add an extension:
Create a new liquibase.database.ext.MariaDBDatabase class in your codebase that extends liquibase.database.core.MySQLDatabase and override the isCorrectDatabaseImplementation(DatabaseConnection conn) method that returns true if conn.getDatabaseProductName() equals whatever the MariaDB jdbc driver is returning.
You may also want to override the getDefaultDatabaseProductName() to return "mariadb" instead of MySQL so you can differentiate it with contexts

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

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 to use ON DUPLICATE KEY on hsqldb 2.3.4

According to the update on hsqldb.org listed here: http://hsqldb.org/web/features200.html
It now supports the mysql syntax ON DUPLICATE KEY in hsqldb 2.3.4, yet Im still getting sql errors when trying to run it. If im reading correctly I may need to set certain flags. But I cant find what to set to be able to use this synatx.
MySQL compatibility is documented in the Guide http://hsqldb.org/doc/2.0/guide/compatibility-chapt.html#coc_compatibility_mysql
You need to execute SET DATABASE SQL SYNTAX MYS TRUE or the equivalent URL property sql.syntax_mys=true to enable it.

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>

How can I programmatically run arbitrary SQL statements against my Hibernate/HSQL database?

I'm looking for a way to programmatically execute arbitrary SQL commands against my DB.
(Hibernate, JPA, HSQL)
Query.createNativeQuery() doesn't work for things like CREATE TABLE.
Doing LOTS of searching, I thought I could use the Hibernate Session.doWork().
By using the deprecated Configuration.buildSesionFactory() seems to show that doWork won't work.
I get "use lacks privilege or object not found" for all the CREATE TABLE statements.
So, what other technique is there for executing arbitratry SQL statements?
There were some notes on using the underlying JDBC Statement, but I haven't figure out how to get a JDBC Connection object from Hibernate to try that.
Note that the hibernate.hbm2ddl.auto=create setting will NOT work for me, as I have ARRAY[] columns which it chokes on.
I don't think there is any problem executing a create table statement with a Hibernate native query. Just make sure to use Query.executeUpdate(), and not Query.list() or Query.uniqueResult().
If it doesn't work, please tell us what happens when you execute it, and join the full stack trace of the exception and the SQL query you're executing.
"use lacks privilege or object not found" in HSQL may mean anything, for example existence of a table with the same name. Error messages in HSQL are completely misleading. Try listing your tables using DatabaseMetadata - you have probably already created the table.