Can applied Liquibase changesets be replaced - mysql

If I use a mysql database with utf-8 and InnoDB but liquibase causes an error because row size of a table is too large.
I have a released changeset which causes the error because it exceeds the row size.
<changeSet author="author" id="id">
<addColumn tableName="TABLE">
<column name="COLUMN" type="VARCHAR(5000)" /> <!-- Row size is too long -->
</addColumn>
</changeSet>
The solution for the problem with the row size is to change "VARCHAR(5000)" into "TEXT", but the problem is that this changeset has been executed on some systems so i cannot just change it here.
Is there any solution to tell liquibase that it should replace the changeset by another if not yet executed?

OK found a solution for this. It is possible to use validCheckSum to tell Liquibase that this changeset has been modified.
I just modified the changeset and added the tag like this.
<changeSet author="author" id="id">
<validCheckSum>oldChecksum</validCheckSum>
<validCheckSum>newChecksum</validCheckSum>
<addColumn tableName="TABLE">
<column name="COLUMN" type="TEXT" />
</addColumn>
</changeSet>
After that liquibase accepted the change set as executed so it was possible to make a new changeset which modifies the column.
<changeSet author="author" id="anotherId">
<modifyDataType tableName="TABLE" columnName="COLUMN" newDataType="TEXT" />
</changeSet>

Related

How to manually increment UNIX_TIMESTAMP() when insert a new row with liquibase?

I have a cart table with a cart_number column which I would like to be the current time in milliseconds.
I've created a database change log file and everything works fine but the problem is that the span of time between 2 inserts is too short to get a "unique" cart_number. Here is the liquibase insert:
<property name="now" value="UNIX_TIMESTAMP()" dbms="mysql"/>
<changeSet id="2" author="Me">
<insert tableName="cart">
<column name="user_id" value="1"/>
<column name="cart_number" valueDate="${now}"/>
</insert>
<insert tableName="cart">
<column name="user_id" value="2"/>
<column name="cart_number" valueDate="${now}"/>
</insert>
</changeSet>
And I know this isn't a solution, but I've tried to write something like ${now+1} but it didn't worked. Any help would be appreciated. Thank you in advance.

Autorollback for Liquibase Changeset

I am trying out Liquibase in view of trying to automate Database Deployments and get some structure around Database changes.
Here is my Changeset which i wanted to evaluate for auto rollbacks. Now, i am trying to introduce an error in the foreign key constraint and was expecting that the rollback statements run after the error and it deletes the column from the customer table as well.
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd">
<preConditions>
<dbms type="mysql" />
</preConditions>
<changeSet id="002" author="Hrishi" failOnError="true"
runInTransaction="true">
<preConditions onError="HALT" onFail="HALT"
onFailMessage="The table gender doesnt exist. Precondition not met.">
<not>
<columnExists tableName="customer" columnName="gender_id" />
</not>
<and>
<not>
<foreignKeyConstraintExists foreignKeyName="fk_gender_customer"/>
</not>
</and>
</preConditions>
<addColumn tableName="customer" schemaName="sakila">
<column name="gender_id" type="smallint(1)" />
</addColumn>
<addForeignKeyConstraint constraintName="fk_gender_customer"
referencedTableName="gender" baseColumnNames="gender_id"
baseTableName="customer" referencedColumnNames="invoking_an_error" />
<rollback>
<dropColumn tableName="customer" columnName="gender_id" />
<dropForeignKeyConstraint baseTableName="customer"
constraintName="fk_gender_customer" />
</rollback>
</changeSet>
What really though does happen is that there is no entry in the DATABASECHANGELOG table. Which i expected, but then the column in the customer table still exists.
I did look at the help on the addColumn url on liquibase documentation and MySQL is supported.
http://www.liquibase.org/documentation/changes/add_column.html
Is my understanding correct that it should autorollback all the statements in the changeset?
EDIT : I think the problem in this specific case is that the ALTER table is a DDL statement. From the documentation of MySQL cannot-roll-back.
I'm not sure of the command you issued, but the changes listed in the <rollback> section of the changeset do not happen automatically when an error happens. When an error happens, liquibase tries to roll back the database transaction, but if some things have already been committed, you can get into an inconsistent state. The <rollback> tag is there so that you can manually roll back changes by using the liquibase rollback command.

Liquibase modifyDataType fails on Mysql

I don't seem to be able to execute a type modification on a Mysql 5.7.10 instance (it works with H2 though).
Here are the changeset steps that are involved with the regarded field:
Creation:
<column name="last_modify_time" type="bigint">
<constraints nullable="false" />
</column>
Modification:
<modifyDataType tableName="USER" columnName="last_modify_time" newDataType="timestamp" />
the error msg in Mysql is
Invalid default value for 'last_modify_time' [Failed SQL: ALTER TABLE USER MODIFY last_modify_time timestamp]
Manually modifying the the request to the following works:
ALTER TABLE USER MODIFY last_modify_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
I don't really understand why Mysql needs the default value. Maybe it's an edge case with the version 5.7.10 (using the default configuration options).
In any case Liquibase should be able to handle it.
I've tried to add/remove default value prior to the modifyDataType, without success.
when you are modifying type of a column using liquibase which is non nullable ... you have to first make it nullable and then modify it use safe modify
It turns out that an exactly identical modification on a field named create_time, placed right before, didn't cause any problem. Swapping the two changsets order did solve the problem.
<changeSet id="11" author="author">
<comment>rename last_modify_time to last_modified_date and change type to timestamp</comment>
<modifyDataType tableName="USER" columnName="last_modify_time" newDataType="timestamp" />
<renameColumn tableName="USER" oldColumnName="last_modify_time" newColumnName="last_modified_date" columnDataType="timestamp" />
</changeSet>
<changeSet id="12" author="author">
<comment>rename create_time to created_date and change type to timestamp</comment>
<modifyDataType tableName="USER" columnName="create_time" newDataType="timestamp" />
<renameColumn tableName="USER" oldColumnName="create_time" newColumnName="created_date" columnDataType="timestamp"/>
</changeSet>
I still can't explain what happend, I'm happy enough to get it working. If someone wants to reproduce the error I'd be happy to help.

Boolean insertion without quotes - Liquibase over MySQL

I need to insert new record on this users table with Liquibase. It works well on PostgreSQL, but when it comes to MySQL the SQL output has quotes for the boolean field (u_administrator). Like this:
INSERT INTO users (u_name, ..., u_administrator) VALUES ('Administrator',..., '1')
<changeSet author="jmartins" id="AdminInsertion">
<insert tableName="users" dbms="postgresql">
<column name="u_name" value="Administrator"/>
...
<column name="u_administrator" value="true"/>
</insert>
<insert tableName="users" dbms="mysql">
<column name="u_name" value="Administrator"/>
...
<column name="u_administrator" value="1"/>
</insert>
</changeSet>
So I can I force liquibase to output "u_administrator" without quotes so it can insert correctly on MySQL. Do I need to go for a < sql> custom command?
Thanks anyway.
Use valueBoolean instead of value to tell liquibase it needs to use the database-specific value for a boolean value.

Liquibase script for MYSQL

I have a liquibase xml script. When I run it on Postgres I don't face any problem but when I run it for MYSQL it gives error when the structure is of the following type:-
<insert tableName="user_table">
<column name="id" valueComputed="(select max(id)+1 from user_table)"/>
<column name="name" value="someName"/>
</insert>
When the above script is executed for MYSQL it gives error:-
You can't specify target table 'user_table' for update in FROM clause.
I found a solution to this by using alias like this :-
<insert tableName="user_table">
<column name="id" valueComputed="(select max(id)+1 from (Select * from user_table) t)" />
<column name="name" value="someName"/>
</insert>
But there are thousands of entries like this. Is there any generic way of doing it so that I don't have to change the script at so many places. Thanks.
The easiest approach would be to just update the XML, either with an simple XML parser program or even a regexp search and replace in your text editor.
Alternately, you can override the standard liquibase logic to look for that particular valueComputed pattern and replace it. There are a couple points you could make the change at:
Override the liquibase.parser.core.xml.XMLCHangeLogSAXParser class, probably the parseToNode() method to search through the generated ParsedNode for valueComputed nodes
Override the liquibase.change.core.InsertDataChange class generateStatements() method or addColumn() method to replace valueComputed fields.
See http://liquibase.org/extensions for more info on writing extensions.