Liquibase: How to store PostgreSQL procedures and triggers

Liquibase is an open source, database-independent library for tracking, managing and applying database changes written in Java and distributed as JAR (Java archive). There are many tools to version code, most popular: SVN, GIT, Mercurial.

Many product managers faced the problem with versioning the database during application development. The solution is Liquibase.

Although tool is very usefull (it can track changes related with tables, views, columnt, indexes, foreigns), there are few limitations. One of these there is stored procedures. Liquibase cannot track stored procedures depends on database. The solution is to use custom query executon avaliable in liquibase.

To create custom stored procedure in Liquibase, just make a simple XML file (testChanges.xml):

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd">
    <preConditions>
      <dbms type="postgresql" />
    </preConditions>
    
    <changeSet author="Athan (generated)" id="1360329703893-1-1">
        <createProcedure>
CREATE OR REPLACE FUNCTION TestFunction() RETURNS trigger AS $proc$
BEGIN
  ...
END
$proc$ LANGUAGE plpgsql;
        </createProcedure>
    </changeSet>
</databaseChangeLog>

And type:

liquibase --changeLogFile testChanges.xml --url=jdbc:postgresql://localhost:5432/dbname --username=postgres --password=root update

Your procedure will appear in database.

WARNING! You should remember that Liquibase provides rollback to version, tag or count of changes. For custom SQL there are no rollback actions (such us for create table – drop table). You have to provide a rollback SQL.

So just add a <rollback> tag to yout changeSet:

        <rollback>
DROP FUNCTION TableOffersStateRealisedUpdate();
        </rollback>

Whole XML should look like:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd">
    <preConditions>
      <dbms type="postgresql" />
    </preConditions>
    
    <changeSet author="Athan (generated)" id="1360329703893-1-1">
        <createProcedure>
CREATE OR REPLACE FUNCTION TestFunction() RETURNS trigger AS $proc$
BEGIN
  ...
END
$proc$ LANGUAGE plpgsql;
        </createProcedure>
        <rollback>
DROP FUNCTION TableOffersStateRealisedUpdate();
        </rollback>
    </changeSet>
</databaseChangeLog>

Same case with triggers:

<changeSet author="Athan (generated)" id="1360329703893-2-1">
    <sql>
CREATE TRIGGER "tableoffersstaterealisedupdate" AFTER INSERT OR UPDATE ON "public"."offers"
FOR EACH ROW
EXECUTE PROCEDURE "tableoffersstaterealisedupdate"();
    </sql>
    <rollback>
DROP TRIGGER "tableoffersstaterealisedupdate" ON "public"."offers"
    </rollback>
</changeSet>

Hope this helped you.