WHERE clause in liquibase changelog to create database - sql

How can I generate this SQL statement in my changelog file with liquibase?
CREATE UNIQUE INDEX RL_UK on RUN_LOG(status) WHERE status = 'R'
I have tried it like this but it doesnt work:
<changeSet author="Ferid (generated)" id="1528876614155-232">
<createIndex indexName="RL_UK" tableName="RUN_LOG" unique="true">
<where> status='R'</where>
</createIndex>
I dont use this changelog file to update the database but to create it.

The clause is not available.
As suggested in comments, you could write custom SQL statments with tag but you will have to take care of the rollback yourself :
<changeSet author="Ferid (generated)" id="1528876614155-232">
<sql>
CREATE UNIQUE INDEX RL_UK on RUN_LOG(status) WHERE status = 'R';
</sql>
<rollback>
<sql>
DROP INDEX RL_UK on RUN_LOG;
</sql>
</rollback>
</changeSet>
Although I would personnally suggest that you use the tag properly without the WHERE CLAUSE because :
I can't understand what you are trying to achieve with this where clause. Espacially since it is on the same column
Your changeset probably won't be compatible with all databases supported by Liquibase so it will work with Postgres and fail with Oracle for example. The WHERE CLAUSE in the CREATE INDEX statement is probably not even ANSI-SQL
You add bothering about the rollback for a very simple statement => not worth. Auto-rollback is available on every supported databases for this tag. Check it here : http://www.liquibase.org/documentation/changes/create_index.html

Related

Rename Schema Using Liquibase

Assume that V1 of the Liquibase change set was defined like so:
<changeSet author="newco" id="create-old_table_name">
<createTable tableName="old_table_name">
...
</createTable>
</changeSet>
V1 didn't define a schema name, therefore as I understand it has a default value, but in V2 I want to specify a schema name:
<changeSet author="newco" id="update-v2-rename-old_table_name">
<renameTable newTableName="old_table_name"
oldTableName="old_table_name"
schemaName="example"/>
</changeSet>
But this doesn't work and throws an exception stating that the table name didn't change. Is it not possible to change the schema?
What DB change are you trying to achieve here?
If you want to move a table from one schema to another you will need to use a SQL change as the "renameTable" change type is only for renaming a table within the same schema.
See e.g. how to move tables from public to other schema in Postgres for the SQL needed to achieve that move in psql
Something like this?
<sql>
ALTER TABLE old_table_name
SET SCHEMA example;
</sql>

Postgres: How to create index concurrently using liquibase

Looking at the liquibase documention http://www.liquibase.org/documentation/changes/create_index.html, CIC is not possible with create index, as liquibase doesn't have a tag to specify concurrent option.
Is there a way to create index concurrently with liquibase?
You can specify runInTransaction as false to create the index concurrently.
Creating a concurrent index must be done with the arbitrary sql change:
<changeSet runInTransaction="false" id="10-add-widgets-kind-index" author="username">
<sql dbms="postgresql">
CREATE INDEX CONCURRENTLY
IF NOT EXISTS idx_widgets_kind
ON widgets(kind)
</sql>
</changeSet>
This is a combination of a_horse_with_no_name's comment and TheDude's answer.
The previous answers do the job. I would like to offer an alternative that doesn't directly answer the OP's question, but does offer a solution with the same end result with some added advantages. I feel it is good to show other options for people that stumble upon this answer like I did.
In order to create the index using only Liquibase, you would need to use the <sql> tag. I caution against this as it can cause undesired consequences in the event that you use a different database for any reason (development, evaluation, testing, etc). The sql statement will be skipped and you can be left thinking that the index was added when in reality it was not.
Additionally, this can lead to a less controlled migration, assuming you are running this on a production system without taking it down for maintenance and the migration being part of the build process.
I would propose creating the index directly on Postgres and adding the index migration normally using Liquibase and a precondition check.
First, add the index manually:
CREATE INDEX CONCURRENTLY widgets_kind_idx ON widgets (kind);
And then add to your Liquibase changeSet:
<changeSet id="10-add-widgets-kind-index" author="username">
<preConditions onFail="MARK_RAN">
<not>
<indexExists indexName="widgets_kind_idx" />
</not>
</preConditions>
<createIndex tableName="widgets" indexName="widgets_kind_idx">
<column name="kind" />
</createIndex>
</changeSet>
This offers the ability to add the index in any manner desired and keeps your Liquibase migrations in a known state. A fresh database being setup would not require the CONCURRENTLY keyword.

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>

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.

Compress Column in Liquibase

I am looking to compress a column through Liquibase and I haven't been able to find any examples of this on the Liquidbase site.
I was wondering if anyone has an example of this?
You can add custom SQL statements to Liquibase change logs using the <sql> element and use the dbms attribute on change sets to define for which databases they are meant to be run.
<changeSet id=".." dbms="oracle">
<sql>
alter table foobar move compress;
</sql>
<rollback>
<sql>
alter table foobar nocompress;
</sql>
</rollback>
</changeSet>
You can use modifyDataType
<changeSet author="liquibase-docs" id="modifyDataType-example">
<modifyDataType catalogName="cat"
columnName="id"
newDataType="A String"
schemaName="public"
tableName="person"/>
</changeSet>