Trying to figure out how rollbacks work with formatted SQL and Liquibase.
Working from the quickstart on the Liquibase site, I was able to create a table in my database. Then started working on the rollback. Just as a manual test, once my table was created, I tagged the change in the DATABASECHANGELOG table. Ran Liquibase rollback <mytag> and it said it completed it successfully, but the table wasn't modified.
--liquibase formatted sql
--changeset user:1
create table addresses (
address VARCHAR(45),
city VARCHAR(45),
zip VARCHAR(10)
);
--rollback drop table addresses;
Liquibase said the rollback completed successfully, but the table wasn't dropped.
The tagging concept in Liquibase seems to confuse many people. Tagging is used to mark a known good state, so if you tagged it after deploying a change that created a table, and then said rollback to that tag, it did things 'correctly' in its way of thinking.
Here is the tiny bit of documentation on that (I added the emphasis on after):
Tag
Specifying a tag to rollback to will roll back all change-sets that
were executed against the target database after the given tag was
applied. See the “command line” documentation for how to tag your
database.
To test the rollback of the table in your example, you would need to use the rollbackCount command or the rollbackToDate command.
Related
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
I use liquibase tool to manage a postgres database. I work as the following :
I have a solution composed of different folders containing SQL scripts responsible for schema creation, tables creations, types creation, procedures creation, etc... Then, I have a ChangeLog file in xml format, containing the following informations :
includeAll path="#{Server.WorkingDirectory}#/02 - Schema" relativeToChangelogFile="false"
includeAll path="#{Server.WorkingDirectory}#/03 - Types" relativeToChangelogFile="false
includeAll path="#{Server.WorkingDirectory}#/04 - Tables" relativeToChangelogFile="false"
includeAll path="#{Server.WorkingDirectory}#/05 - Fonctions" relativeToChangelogFile="false"
includeAll path="#{Server.WorkingDirectory}#/06 - Stored Procedures" relativeToChangelogFile="false"
I run liquibase via command line :
liquibase --changeLogFile=$(Changelog.File.Name) --driver=$(Driver.Name) --classpath=$(Driver.Classpath) --url=$(BDD.URL) --username=$(BDD.Login) --password=$(BDD.Password) update
This enable Liquibase to take all the SQL scripts in the different folders listed in the changelogFile, compare it with the current database at url $(BDD.URL), and generate a delta script containing all the SQL queries to be executed to have a database corresponding to my solution.
This works well when I add new scripts (new tables or procedures) or modify existing scripts, my database is correctly updated by the command line, as expected. BUT it does not do anything when I delete a script from my solution.
To be more factual, here is what I want :
I have a SQL file containing the query "CREATE TABLE my_table" located in the folder "04 - Tables".
I execute the update command above, and it creates the table "my_table" in my database.
I finally do not want this table in my database any more. Thus I would like to simply remove the corresponding SQL script from my solution, and then run again the "update" command to simply remove my table in my database, generating automatically a "DROP TABLE my_table" by the liquibase "update" command. But this is not working as Liquibase doesn't record any change when I remove a sql file (whereas it does when I add or modify a file).
Does anyone know a solution to this ? Is there a specific command to drop an element when there is no "CREATE" query for this element, in a SQL solution ?
Many thanks in advance for you help :)
You will need to explicitly write a script to drop the table.
Other option is to rollback the change IF YOU HAVE Specified the Rollback SQL as part of your original SQL script.
There is a Pro Version option to rollback a single update , with free / community version, you can rollback last few changes in sequence
ex; I did "liquibase rollbackCount 5" will rollback the last 5 changes that were applied ONLY IF I HAD Coded the rollback sql needed as part of my script.
My Sql script sample that included the code to rollback is
--rollback drop TABLE test.user1 ; drop table test.cd_activity;
CREATE TABLE test.user1 (
user_type_id int NOT NULL
);
CREATE TABLE test.cd_activity (
activity_id Integer NOT NULL
userid int);
I perform a liquibase update command with a given ChangeLog XML file and tag it with tag1.
For example:
liquibase --driver=org.postgresql.Driver --url=... --changeLogFile=change1.xml update
liquibase --driver=org.postgresql.Driver --url=... --tag tag1
I then perform several additional updates commands with other ChangeLog XML files:
liquibase --driver=org.postgresql.Driver --url=... --changeLogFile=change2.xml update
liquibase --driver=org.postgresql.Driver --url=... --changeLogFile=change3.xml update
Now I would like to rollback to tag1:
liquibase --driver=org.postgresql.Driver --url=... --changeLogFile=??? rollback tag1
Which file should I specify in --ChangeLogFile? Is there a way to define multiple files? Is there a way Liquibase can store embed the rollback commands without the need to supply the update XMLs?
Rather than using multiple changelog files from the command line, typical usage is to just have one changelog file that is used for the whole application. It is possible to have multiple changelogs, but in that pattern you have a single 'master' changelog that then includes other changelogs. see https://www.liquibase.org/bestpractices.html for some examples of this.
When rolling back, liquibase needs to know what the actual changes are, so it can then do something else to do the rollback. The DATABASECHANGELOG table does not store the actual contents of each changeset that has been applied, it just keeps the id, author, filepath, and a checksum. It has no way of knowing that changeset id 123778 by steve in file changelog.xml was a create table or an alter column or anything. So the changelog file MUST be there to be able to roll things back.
For certain change types like 'create table', Liquibase can 'automatically' generate a rollback for you - it is just 'drop table'. But if the change was something like 'drop table', it cannot generate a rollback - drop table command only has the name of the table to drop, and doesn't know what columns were in that table, etc.
So that is why Liquibase requires that you always supply a changelog file, and if you want to be able to do rollbacks, you may also need to specify how to roll back each change.
When using the includeAll option in the databaseChangeLog, is there any way to use rollback or tagging? It seems really nice to have all my changes in file 01.sql to 99.sql run in order. Do I have to go back to specifying the individual files and rollbacks to make this work?
You can use SQL Format
e.g.:
--changeset nvoxland:1
create table test1 (
id int primary key,
name varchar(255)
);
--rollback drop table test1;
As of Liquibase 2.0, Liquibase includes support for “plain SQL”
changelog files. These changelogs may be included from XML changelogs
and may contain arbitrary SQL statements. The statements are converted
to custom_sql refactorings.
Formatted SQL files use comments to provide Liquibase with metadata.
Read more about SQL Format
How do I find out what application or SP is modifing the values in a config table? I thought I had isolated the app that was responsible but these particular values keep chnging back to true when I keep modifying them to be false.
First, create a logging table:
CREATE TABLE modlog(
datestamp smalldatetime,
username varchar(255) NOT NULL DEFAULT SYSTEM_USER
);
Then create an UPDATE trigger on your table:
CREATE TRIGGER mytable_mods ON mytable FOR UPDATE AS
INSERT INTO modlog(smalldatetime) VALUES (GETDATE());
Just peek into the modlog table to figure out which user is updating the table, and when. You could get fancy and also log particular fields being updated.
Another approach would be to set up a trace in SQL Server Profiler, filter the heck out of it so it only returns updates on that table, and keep it open until something happens.
If your applications include the ApplicationName parameter in their connection strings, you can use App_Name() instead of SYSTEM_USER, which will log the application name, removing the extra detective work. Knowing the user might still be useful so you can figure out what they are doing to trigger the update.
Create a trigger to roll back the update. Wait for the app to error out. It can be a very simple trigger:
CREATE TRIGGER BugOffRogueProgram
ON MyConfigTable
FOR UPDATE
AS
BEGIN
ROLLBACK TRAN
END
The answers provided so far are absolutely on the spot - that's the way to do it in SQL Server 2005.
Just as a brief teaser: in SQL Server 2008, there's a new feature called Change Data Capture to support this exact scenario "out of the box" without the need to write triggers and update tables yourself. Quite handy!
Marc