Rename Schema Using Liquibase - 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>

Related

How to rollback a drop column in Liquibase

Which is the best way to rollback this liquibase script?
<changeSet author="me" id="drop_column_example">
<dropColumn tableName="BLABLA">
<column name="example"/>
</dropColumn>
</changeSet>
Usually when I drop a table, I restore all the data from a duplicate temporary table created before the drop, but how can I manage this?
PS: I need to restore all the old data in that column.
Liquibase provides commands to undo changes made to database. The intention of a rollback script is to return the database to a previous specified point in time.
There are two categories of Liquibase operations, resulting in a different generation of a rollback statement:
1. Automatic - where migration can deterministically generate steps required for rolling back
2. Manual - where we need to issue a rollback command because migration instruction cannot be used to identify the statement deterministically
For example, the rollback of a “create table” statement would be to “drop” the created table. This can be determined without a doubt, and therefore the rollback statement can be autogenerated.
On the other hand, the rollback statement for a “drop table” command is not possible to be determined. It is not possible to determine the last state of the table, and therefore the rollback statement can't be autogenerated. These types of migration statements require a manual rollback instructions.
Read more about it here and here.
In your case, rollback statement for a “drop column” command is not possible to be determined. You will manually have to write an “alter table ADD column” statement for your table.
For your example, the query will be :
ALTER TABLE BLABLA ADD COLUMN example DATATYPE;
So the complete changeset would look like this :
<changeSet author="me" id="drop_column_example">
<dropColumn tableName="BLABLA">
<column name="example"/>
</dropColumn>
<rollback>
ALTER TABLE BLABLA ADD COLUMN example COLUMN_DATATYPE;
</rollback>
</changeSet>
To restore data in the column, you will have to create a changeset to copy and insert the data from a duplicate temporary table (similar to the way you mentioned for “drop table”) to this created column. Make sure you execute your data insertion in column after the rollback query for “drop column” is executed successfully.
You could either create a separate changeset or could include the logic to copy and insert old data for restoring into new column in the above changeset as below:
<changeSet author="me" id="drop_column_example">
<dropColumn tableName="BLABLA">
<column name="example"/>
</dropColumn>
<rollback>
ALTER TABLE BLABLA ADD COLUMN example COLUMN_DATATYPE;
<!-- Logic to restore old data goes here OR include the changeset created separately for data restoration here (After creating the dropped column) -->
</rollback>
</changeSet>
Added logic to copy the old data from temporary table to new created table -
INSERT INTO `New_Table` (`example`) VALUES
(SELECT `example` FROM `Temporary_Table` WHERE
`New_Table`.`Some_Common_Column` = `Temporary_Table`.`Some_Common_Column`);
You need to maintain the old data into a table in such a way that you can insert it at the time of restoration with proper mapping to the primary key column or some column which is common between the newly created table and backup table. Without having such structure you won't be able to map the required column data for data restoration in new table column.

How to use different tablespaces for tables and indexes with Liquibase

I am using Liquibase 4.0 and I am executing DB changes using SQL scripts. So I am pointing the SQL files path in the master change log to execute the changes via Liquibase.
Now I need to segregate them since I have a requirement to have different tablespace for both tables and indexes.
Hence I tried to find an option for parameterization in Liquibase, but could not find so.
My Masterchangelog for the change looks like:-
<changeSet author="sanjib" id="01_PR_001">
<sqlFile dbms="oracle"
endDelimiter=";"
path="../scripts/PR_001/PR_001.sql"
relativeToChangelogFile="true"
splitStatements="true"
stripComments="true"/>
<rollback>
<dropTable cascadeConstraints="true" tableName="PERSON"/>
</rollback>
</changeSet>
The Liquibase.properties file looks like this:-
driver: oracle.jdbc.OracleDriver
classpath: ojdbc6.jar
url: jdbc:oracle:thin:#localhost:1521/xe
databaseChangeLogTablespaceName:DATA
username: sanjib
Now the main issue is that the table defined in the SQL script is not created under the user's tablespace. Instead, it is using the username from the properties file as tablespace name, hence facing the issue that Table cannot be created since tablespace is missing, which is an obvious error. Hence to create the DATABSAECHANGELOG & DATABASECHANGELOGLOCK table, I had to add the property 'databaseChangeLogTablespaceName' in the properties file, but the tables and indexes are not getting created.
So please help in fixing the 2 issues.
How to point the installation via Liquibase to use the user's default tablespace instead of using the username as tablespace.
Can we parameterize the Table's tablespace and index's tablespace using the above setup as defined in the XML file?
To use different tablespaces for tables and indexes, liquibase property substitution is the way to achieve it. You can declare 2 tablespaces name properties, one for tables and the other for indexes. These properties can be declared in liquibase.properties file or at the start of your changelog itself. You can then access it using the syntax ${property_name} and use it at the places to provide tablespace name.
From liquibase docs on this link, you can specify tablespace name at the time of creating table. Example for the same is as below:
(Example with declaring a property in liquibase.properties file)
Add a property in your liquibase.properties file :
tablespace: tablespaceQA
<changeSet id="2" author="liquibase">
<createTable catalogName="department2"
remarks="A String"
schemaName="public"
tableName="person"
tablespace="${tablespace}">
<column name="address" type="varchar(255)"/>
</createTable>
</changeSet>
the property ${tablespace} gets substituted with it's value as configured in your liquibase.properties file or with the value you pass when you run liquibase update command (liquibase -Dtablespace='tablespaceQA' update)
(Example with declaring a property at start of your changelog)
For indexes, you can declare a property at the start of your changelog (or in liquibase.properties file).
<property name="index_tablespace" value="INDEX" dbms="oracle"/>
now, use the property in createIndex tag as folows:
<createIndex indexName="idx_firstname" tableName="person" tablespace="${index_tablespace}"/>
For more details, visit the comments on this post.
Cheers!!
I don't remember how exactly sql files works, but if I'm not mistaken then replacing properties should work also in sql files. So in your sqlfile you can do something like this:
create index somespecificindex tablespace ${indexTablespace} ...
Then you can define some property that will be substituted. So for example something like this
<property name="indexTablespace" value="indexes" />
And when you execute your migration then everything should work.

Liquibase: Cache for column

I would like do the following by using liquibase:
alter table SCHEMAX.TABLEX modify lob (COLUMNX)(CACHE);
Does anybody know if this is possible?
Thanks
You can specify any kind of SQL in an sql change set:
Example:
<changeSet author="liquibase-docs" id="sql-example">
<sql dbms="mysql">
alter table SCHEMAX.TABLEX modify lob (COLUMNX)(CACHE);
</sql>
</changeSet>
you should specify the supported dbms, because the SQL might not execute on other DBMSs, as it might use vendor specific commands.

WHERE clause in liquibase changelog to create database

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

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>