How to rollback a drop column in Liquibase - 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.

Related

In Liquibase, how can I rollback changes made to "rerunnable" (runOnChange=true) changesets?

In my liquibase project, I have an sql view that is defined within a file called create_myview.sql.
Each time a change is made to the view, i.e. a column is dropped or renamed, instead of creating a new changeset that contains an ALTER statement, the view definition itself in this file is changed.
This file has the runOnChange attribute set to true so that when changes are made, the view is dropped and recreated the next time liquibase update is run. As such the same file can be run over and over when its contents change, i.e. it is "rerunnable".
Since previous definitions of the view are overwritten with each change, rolling back to a previous version presents a challenge and I am unable to work out the best way to do this.
Currently the only way that I am storing the previous view definitions is through git branches, i.e. with each view change I create a new branch.
Ideally I would like to checkout an old branch and be able to rollback to the definition that is currently checked out. I would like to be able to hop between versions easily.
Other non-rerunnable changesets defined in my project use a --rollback tag that is specified in the sql file and provides the inverse sql operation to each table change i.e. if a column is added to a table using an alter statement, then the inverse sql statement specified looks like:
-- rollback ALTER TABLE x DROP COLUMN name;
The equivalent of using this tag in my rerunnable view file would be to copy the entire previous view definition into this rollback tag, which doesn't seem like it would be best practice.
However, liquibase rollback doesn't seem to work without the --rollback tag being specified.
The file structure:
myDB/
changelog/
ver1/
rerunnable/
create_myview.sql
rerunnable.myDB.xml
myDB.changelog-root.xml
This is how I would make changes and update my view from one version to the next.
checkout new branch
git checkout -b c-01
add changes to view
create_myview.sql:
-- changeset john:c-01 runOnChange:true
DROP VIEW my_view IF EXISTS my_view;
CREATE my_view AS
SELECT name, date
FROM my_table;
...
update the changeset attributes
rerunnable.myDB.xml
<changeSet author="john" id="c-01">
<tagDatabase tag="1.0.0"/>
</changeSet>
Run liquibase update.
Next, an update is made to the view when the date column is dropped.
git checkout -b c-02
-- changeset john:c-02 runOnChange:true
DROP VIEW my_view IF EXISTS my_view;
CREATE my_view AS
SELECT name
FROM my_table;
...
<changeSet author="john" id="c-02">
<tagDatabase tag="2.0.0"/>
</changeSet>
liquibase update
At this point, the view in the database is up to date with the latest update, without the date column, and the databasechangelog looks like:
id
author
orderexecuted
md5sum
description
tag
c-01
john
1
83h8hs...
tagDatabase
1.0.0
c-01
john
2
ln9n2b1...
sql
c-02
john
3
ib309bd...
tagDatabase
2.0.0
c-02
john
4
lmxo21...
sql
From this point I am unable to rollback to how the view was at c-01/1.0.0.
The behaviour that I expect/hope is possible would be something like:
check out branch c-01
the old view definition is now in the working directory
run liquibase rollback or liquibase update
the view in the database is dropped and recreated with the c-01 schema (with the date column).
The changelog only has the first 2 lines.
Unfortunately, liquibase update does nothing, and liquibase rollback specifies that I need a --rollback statement.
would the runAlways attribute work here as a good solution?
For those not on the Liquibase forums, I answered this question there:
https://forum.liquibase.org/t/how-do-i-manage-rolling-back-changes-made-to-rerunnable-runonchange-true-changelogs-that-contain-stored-logic/7780

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.

Liquibase rollback not working in Sprintboot 2.0

How does liquibase rollback works with springboot application? Would appreciate your inputs.
Here is what i tried - I am creating TableA and TableB in Oracle within a changeset.
TableB already exists in the database, I expect liquibase to rollback TableA as changeset fails while creating TableB but liquibase creating TableA and failing with the below error and never executes rollback block, which is strange:
Caused by: liquibase.exception.DatabaseException: ORA-00955: name is already used by an existing object
Liquibase Configuration:
<changeSet id="rollback" author="test_user">
<validCheckSum>any</validCheckSum>
<sqlFile path="db/changelog/changes/DML/ddl.sql"/>
<sqlFile path="db/changelog/changes/DML/ddl.sql"/>
<rollback> drop table TABLEA;</rollback>
<rollback> drop table TABLEB;</rollback>
</changeSet>
ddl.sql
CREATE TABLE TABLEA
(
TEST_COL VARCHAR2(100)
);
dml.sql
CREATE TABLE TABLEB
(
TEST_COL VARCHAR2(100)
);
Looks like you have a wrong perception of what the rollback is for. It's not for "reverting one big transaction within a whole changeSet". It's for reinstating some past schema state.
According to Oracle documentation
Oracle Database issues an implicit COMMIT before and after any data
definition language (DDL) statement.
So after every create table statement execution, Oracle commits the transaction.
If the error happens during the execution of a changeSet, liquibase can rollback only a failed transaction, which it does.
Check out this thread
Liquibase does not automatically roll back changes made during a deploy if there was an error. Rather, it can roll a database back to a previous schema state when you ask it to. In some cases, it can do that without you explicitly saying how to do the rollback.
It would be better to think of the rollback command in Liquibase as
'undo deploys' rather than a transactional rollback.
Also, check out this liquibase-rollback article on how to use rollbacks.

Run rollback when checksum is changed for changeset in Liquibase

I see an attribute runOnChange that re-runs changeset when it is changed. But is it possible to apply a rollback for this changeset before re-applying automatically?
For example, I have a script that is called from changeset. I made some changes there and want to re-apply, but before it rollback need to be called and after it new version of the script should be applied.
Thank you!
There is no feature to automatically roll back a changeSet on checksum change. Not sure if it is possible in general because if the configuration has changed you don't know what the old value was to roll back.
Depending on what you are doing in your script and your database, can roll back the changes manually in the script and use the liquibase runOnChange="true" changeSet flag.
For example, if you have a script that creates a stored procedure, you could use <changeSet runOnChange="true"> and then define your procedure as "CREATE OR REPLACE"
If you have a script that defines a view, you could add a <sql>IF EXISTS VIEW_NAME DROP VIEW VIEW_NAME</sql>

liquibase does not add script to history when changeset fails

When a changeset is marked as failOnError:false, does liquibase record it as having been applied when it fails?
For example, we have a script that performs a pre-emptive drop table in one changeset and then creates the table in the next changeset. When the script is first run, the drop table statement fails as expected and then the table is created successfully. However, the changeset that attempted the drop table is not added to the databasechangelog table.
Is that expected behavior?
That is the current behavior currently. Depending on the reason for the failure, it can make sense to either continue to retry it or to not.
I created https://liquibase.jira.com/browse/CORE-1766 to add the feature to mark it as failed and not try again.
Currently, your best option would be to add a precondition to the dropTable changeSet with onFail="MARK_RAN"