Can I use <include file="..."/> in a Liquibase rollback section? - liquibase

I couldn't immediately see, in the current documentation I am looking at:
Will the following work in Liquibase, and use the included SQL file for the rollback?
<changeSet author="username" id="85138">
<sql splitStatements="false">
UPDATE some_table ...;
</sql>
<rollback>
<include file="path/to/rollback.sql"/>
</rollback>
</changeSet>
If I've missed the appropriate spot in the documentation, feel free to point out the relevant section.

What you can do is use sqlFile:
<changeset ..>
<sql>...</sql>
<rollback>
<sqlFile path="rollback.sql" />
</rollback>
</changeset>
As mentioned by another post, include is used for changelog composition only - to include other changelogs.

No, that will not work. The include tag is intended for including further changelog XML files, not for including arbitrary information.
It's documented here: http://www.liquibase.org/manual/include
It doesn't say you "can't" specifically, but I tried it - and liquibase will yell at you for it. =)

Related

Smaller Changesets when Generating Changelogs

Is it possible to generate change logs, merging change sets together?
Like this:
<changeSet author="Author" id="1">
<dropNotNullConstraint columnDataType="varchar(255)" columnName="name" tableName="table"/>
<dropNotNullConstraint columnDataType="varchar(255)" columnName="phone" tableName="table"/>
<dropNotNullConstraint columnDataType="bigint" columnName="email" tableName="table"/>
</changeSet>
instead of this:
<changeSet author="Author" id="1">
<dropNotNullConstraint columnDataType="varchar(255)" columnName="name" tableName="table"/>
</changeSet>
<changeSet author="Author" id="2">
<dropNotNullConstraint columnDataType="varchar(255)" columnName="phone" tableName="table"/>
</changeSet>
<changeSet author="Author" id="3">
<dropNotNullConstraint columnDataType="bigint" columnName="email" tableName="table"/>
</changeSet>
If so, can you choose a custom scope? (e.g. all changes in a given table are grouped together, etc.). If not, is there a 'best practice' reason for avoiding grouping them manually?
If you mean command generateChangeLog then no, there is no option to put all changes in one changeLog. However you can create that programatically if you need.
Liquibase should changes per changeSet because each changeSet is executed in one transaction read here. So if you include a lot of changes inside one changeSet and execution of that changeSet fails, it would be harder for you to find on which change it failed. It's better to handle change per changeSet.
note: sometimes I'm also using multiple changes per changeSet (for example remarks (comments) for table or DML (data) modifications) but I prefer to have one DDL per changeSet.

Liquibase add comment to table

I'm currently trying to add comment on tables in liquibase 3.1.1
What I want to do is ALTER TABLE t1 COMMENT = 'New table comment';
I didn't find any help in the documentation of liquibase about this case, there is only help for adding comments on columns.
I am currently thinking of creating a customChange or doing the change by myself with SQL statements but as we are going to migrate from MySQL to Oracle I would like to avoid this solution (or use it in last resort).
Has anyone found another solution for this problem ?
COMMENT ON TABLE is absolutely helpful in this case. See example I provided below along with some tips and pitfalls about editing tables using liqubase.
Continuing the response above, I'd like to pay attention on tag <preConditions> here. It's critical when dealing with tables to ask liqubase whether the certain table exists at the moment you try to edit one. Here's some example of this behavior:
<changeSet id="your_table_id" author="author_name" dbms="postgresql">
<preConditions onFail="MARK_RAN">
<tableExists tableName="table_name" schemaName="schema_name"/>
</preConditions>
<sql>
COMMENT ON TABLE schema_name.table_name is 'YOUR_COMMENT_ON_TABLE';
</sql>
</changeSet>
In case dealing with columns (editing, adding, deleting) ALSO consider asking liqubase the similar way of the existing or absence of the specific column(s):
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="table_name" schemaName="schema_name"
columnName="column_name"/>
</not>
</preConditions>
<sql>
...
</sql>
The example above checks if column NOT present in table and then executes some SQL script.
I solved this problem by creating a custom SQL statement with a <sql> tag (or "sql" key if you are using JSON) in the changeSet (documentation can be found here).
Since I am using PostgreSQL, the SQL syntax for adding a comment to a table is COMMENT ON TABLE table_name IS 'text'. Below is the entire changeSet in XML format:
<changeSet author="gfigueroa" id="add-comment-to-table-xyz">
<sql dbms="postgresql"
endDelimiter=";"
splitStatements="true"
stripComments="true">
COMMENT ON TABLE xyz IS 'This is a test comment';
</sql>
</changeSet>

Can Liquibase ignore checksums for a single changeset?

I’m using Liquibase via the Gradle-Liquibase (v 1.1.1) plugin. I have the following changeset …
<changeSet id="create_my_stored_proc" author="davea" dbms="mysql" runAlways="true">
<sqlFile endDelimiter="//" path="src/main/resources/scripts/create_my_stored_proc.sql" stripComments="true"/>
</changeSet>
Is it possible to set something such that checksums are ignored for this changeset only? The underlying procedure is in a state of flux that could be repeatedly updated and rather than create a new changeset each time, I would like the existing one to run upon every Liquibase build.
You can disable the checks per changeSet using the <validCheckSum> tag with known good values.
For example, if the previous changeset had a checksum like 8:b3d6a29ce3a75940858cd093501151d1 and you wanted to tweak that changeSet (but not re-apply it where this step has already succeeded) then you could use something like this:
<changeSet author="me" id="mychangeset">
<validCheckSum>8:b3d6a29ce3a75940858cd093501151d1</validCheckSum>
<sqlFile ... />
</changeSet>
"RunAlways will still throw a checksum error by default, but you can always use runOnChange=true or any to change that."
Have a look at this ticket raised in liquibase: https://liquibase.jira.com/browse/CORE-2506
So, you could do:
<changeSet id="create_my_stored_proc" author="davea" dbms="mysql" runAlways="true">
<validCheckSum>any</validCheckSum>
<sqlFile endDelimiter="//" path="src/main/resources/scripts/create_my_stored_proc.sql" stripComments="true"/>
</changeSet>
You can add the runAlways and/or runOnChange attributes.

how to recreate an index if it already exists with liquibase

I have a changeset to create an index.
<changeSet author="hilland" id="x-NC-U-y" runOnChange="true">
<createIndex indexName="NC-U-y" tableName="x" unique="true">
<column name="y"/>
</createIndex>
<rollback>
<dropIndex .../>
</rollback>
<modifySql>
<append value=" INCLUDE ( [a],[b]) WITH (DATA_COMPRESSION=page)"/>
</modifySql>
</changeSet>
The problem is that an older version of the index might exist (it will on some targets (dev server, with an older version of this index; no includes), will not on others (eg a fresh deploy to an empty database).
Is there a better way to resolve this so that the script becomes universal, than to include a prefix which says
<sql>IF select {sys.indexes.stuff} is not null {drop the index}</sql>?
Ideally there would be a precondition to drop-if-exists the existing index so that it can be recreated, but i don't think that can be done.
What is the best way to handle this situation?
an alternate solution i thought of would be to have a separate changest "if index v1 exists, drop it" then have a separate v2 changeset, but then that would make rollback hard.
I realize that the run on change condition for the changeset makes rollback hard, but the only rollback i'm concerned with in this case is to and from an empty database, although a pattern which easily accommodates rollbacks to previous version would be welcome also.
You can execute changesets dependent on a precondition. Your changeset could look like this:
<changeSet>
<preConditions onFail="MARK_RAN">
<indexExists indexName="NC-U-y" />
</preConditions>
<dropIndex indexName="NC-U-y" />
</changeSet>
See also the Liquibase Documentation.

Multiple PreConditions

I am implementing a changeset in Liquibase that needs a few different preconditions to be valid before it attempts to run.
Scenario #1: If table A,B,C exists, mark as ran
Scenario #2: If table X,Y,Z doesn't exist, halt execution of changeset
In other words, I need two <preConditions> tags with different onFail clauses. Is this at all allowed in Liquibase? I can't seem to make it work. The docs aren't very informative.
It is not allowed currently. There can be just one block.
Would it work to break it up into two separate changeSets?
Combine them with <or> or <and> like this:
<preConditions>
<or>
<runningAs username="liquibase" />
<runningAs username="sa" />
</or>
</preConditions>
Here is some documentation on preConditions.
A workaround that I've simply used:
create a normal precondition
<!-- pre-condition tests -->
<preConditions onFail="WARN">
<runningAs username="${db.maintenance.user}" />
</preConditions>
create an empty changeSet for another onFail level
<changeSet id="liquibase-pre-1" author="liquibase" runAlways="true" runOnChange="true" runInTransaction="false">
<preConditions onFail="HALT" onFailMessage="...">
<!-- ... -->
</preConditions>
<sql>
select 1 <!-- (Postgresql) or "select 1 from dual" (Oracle) -->
</sql>
</changeSet>