How to use different tablespaces for tables and indexes with Liquibase - sql

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.

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>

Update an existing trigger into Liquibase

I'm very new at Liquibase, and I need some help.
I have an existing trigger that was not capturing all the data; I made some changes to my local Oracle database. Now I need to add those changes into the Liquibase, but I'm lost how to do that.
I know you cannot breach the contract in liquibase by updating the original .xml file directly.
From my understanding, I need to create a new changelog .XML file and then include the path on the other post_migration file.
My confusion is, do I have to drop the original trigger, then create a new file or?
Thanks!
I never create triggers, procedures or even views within the XML file exactly because this makes things more complicated (I think).
I typically move the actual trigger definition into a SQL script (that I can also run separately during development and testing), then include that SQL file from within the Liquibase changelog:
<changeSet id="42" author="arthur" runOnChange="true">
<sqlFile path="triggers/some_trigger.sql"
stripComments="false"
splitStatements="true"
endDelimiter="/"
relativeToChangelogFile="true"/>
</changeSet>
The some_trigger.sql script is stored in git (svn, ...) together with the XML changelog. The runOnChange="true" is the "magical ingredient" here. You don't have to touch the XML file, you just edit the SQL script. During deployment, Liquibase will check if the (SQL) file has changed and run the script if needec.
So, I believe that you create/update/replace the SQL trigger in your local developer database and right now you want to include the liquibase script to the release distribution package of your product.
The liquibase doesn't provide special xml syntax to create triggers, so you will just add a new changeset that holds your pl/sql script inside the <SQL> tag. The script will be the same that you run on your local database.
The example code here:
<changeSet id="1" author="me">
<sql endDelimiter="/">
CREATE OR REPLACE TRIGGER trigger_name before insert
on table1_name for each row
BEGIN
select seq_myseq.nextval
into :new.myid
from dual;
END;
/
</sql>
</changeSet>
This code just compile trigger in the aimed database when you call liquibase update. In most cases, it is enough. But I strictly recommend you to ask your DBA or team led for rules that your team enforced for writing liquibase scripts. For this reason, the result may be much more complicated.

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.

How to update existing changeset

I want to update very old changeset because there is a bug in that. I have updated that changesetid and After running it I got checksum error i.e checksum got modified. Now I have added validCheckSum Tag with this new checkSum. After it is getting successfully I cant not see new changes. Is there any way to update that change set which has already been executed.
Each changeSet tag is uniquely identified by the combination of the “id” tag, the “author” tag so please check whether id and author name already exists or not in DATABASECHANGELOG table. If the combination exists then change the id in your xml file then run the build
If still not works then add runOnChange property in <ChangeSet> and try it
runOnChange - Executes if any changes found it already existing changeset
Example of using runOnChange in changeset with sqlFile tag .
I had a script for Oracle 18 using IDENTITY column type and than have to change it for Oracle 10 which doesn't support IDENTITY column type
<changeSet id="2" author="auto" runOnChange="true">
<sqlFile dbms="oracle" encoding="utf8" endDelimiter=";" path="oracle/230.sql" relativeToChangelogFile="true" splitStatements="true" stripComments="true" />
</changeSet>
If it is a very old changeset as you have stated you should consider not changing the old changeset but add a new changeset instead which fixes the bug since it allows you better control over the versions you have. This is one of the main reasons of using liquibase.

Liquibase alter column constraints

I want to start with a specific example where a table is created with a column having a constraint indicating this column is a foreign key:
<changeSet id="1" author="A1">
<createTable tableName="TABLE_A">
<!-- Other columns -->
<column name="FK_TABLE_B" type="BIGINT">
<!-- the 'TABLE_B' will be renamed soon ! -->
<constraints references="TABLE_B(ID_PK)"
foreignKeyName="JUST_UNIQUE_I_GUESS" nullable="false" />
</column>
</createTable>
</changeSet>
Now a first question: Assume TABLE_B would be renamed to TABLE_NEW... how would i want to change the above constraint so it points to the renamed Tables column ID_PK?
I see a few possibilities:
modify the changeSet itself (no Problem with a H2 inMemory DB... but cmon... thats not the idear right?)
Drop the whole column in my own changeSet and add it again with the new constraint which could make sence since the column name will probably change to FK_TABLE_NEW anyways... but thats not a real possibilitie for a productive environment
somehow alter that constraint -> BUT HOW!? the documentation is no
help at all...
The next question then is why to have this way of constraints if there is a
<addForeignKeyConstraint ...>
as well?
So currently i dont feel Liquibase at all just because this was my very first try to change an existing changeSet and my biggest question is: Is this a issue of not understanding the best practise or acctually a problem of the "not so verbouse" documentation?
Thanks for any help!
What i tried
assumed above constraints definition somehow leads to the same as a when done with a
<addForeignKeyConstraint ...>
tag but with less attributes. So i thought i could just use the
<dropForeignKeyConstraint ...>
tag first delete the ForeignKeyConstraint and then add a new one. But it still tells me it wont find table TABLE_B when trying to execute the first changeSet in a H2 in-memory DB.
My Changeset looked like this:
<changeSet id="1" author="A2">
<dropForeignKeyConstraint baseTableName="TABLE_A"
constraintName="JUST_UNIQUE_I_GUESS" />
<addForeignKeyConstraint
constraintName="JUST_UNIQUE_I_GUESS"
referencedTableName="TABLE_NEW" baseColumnNames="FK_TABLE_B"
baseTableName="TABLE_A" referencedColumnNames="ID_PK" />
</changeSet>
Background Information
Since were currently just building a POC using a in-memory DB only (H2) its no big deal to just change the first changeSet until we have quite of a final scheme-state... but how to deal with such things if you already have a existing DB, millions of entrys and stuff? Currently i highly doubt Liquibase is the right decision for a company with 1k+ developpers without hiring Liquibase Experts...
You should only modify your changesets as long as they (resp. the software/database that go along with the changesets) have not been released.
We keep our changeset files (along with the code) in our source code repository. During development phase everyone is allowed to change changesets defined for the version currently under development.
As soon as the version is released the changeset files are considered to be fixed and should not be changed anymore. (With the release the software is shipped and customers then have a database that reflects whatever the changesets define).
So after release you will have to create new changeSets that
drop the foreign key constraint
rename your table
add a new foreign key constraint
More or less, you need to do exactly what you would do with pure sql on the database as well.
At this point liquibase will more or less just translate your changeSets to sql and apply them to the database. So once you found a way to do your changes with slq it should also be possible to put those into liquibase changesets.