Quantcast
Channel: Liquibase
Viewing all 200 articles
Browse latest View live

Liquibase 3.2.2 Released

$
0
0

Liquibase 3.2.2 has been released. It is a small bugfix release to fix a checksum regression from 3.1 -> 3.2 that was not fixed in 3.2.1.

Issues Resolved:

  • [CORE-1938] – defaultValueNumeric=”0″ or defaultValue=”0″ is translated to 0.0
  • [CORE-1950] – Checksum validation failed after Liquibase upgrade (3.1.1 -> 3.2.0)
  • [CORE-1959] – generateChangeLog without changeLogFile – better error message

As usual, it can be downloaded from the Liquibase download page and is available in the Maven repository as org.liquibase/liquibase-core.

 


Liquibase 3.3.0 and 3.2.3 Released

$
0
0

Liquibase 3.2.3 and 3.3.0 have been released. As usual, they can be downloaded from the Liquibase download page and are available in the Maven repository as org.liquibase/liquibase-core.

Both 3.2.3 and 3.3.0 should be drop-in replacements for 3.2.2. A new batch of Liquibase extensions will be released over the next few days.

New “label” attribute on changeSet

Labels are general purpose way to categorize changeSets like contexts, but working in the opposite way. Instead of defining a set of contexts at runtime and then a match expression in the changeSet, you define a set of labels in the context and a match expression at runtime.

The most common time you would use labels instead of contexts is when the person doing the liquibase update has the knowledge of the types of changeSets to run, not the person writing the changeSet.

Labels can also be applied to modifySql

New change log commands and attributes

  • New “empty” tag for explicitly marking a changeSet as unused
  • New “output” tag for outputting a message during Liquibase update.
  • New relativeToChangeLogFile attribute for loadData and loadUpdateDate
  • New  fullDefinition=true|false attribute on createView to support defining an entire view definition (including “column” names)

Support for clustered/nonclustered indexes and primary keys

A new “clustered=’true|false’” attribute is now avaiable on createIndex and createPrimaryKey to control whether they should be created as clustered or not.

And More

  • Saving of “remarks” in MySQL and MSSQL
  • Improved data type handling
  • Performance improvements
  • Official RPM and DEB packages built with release
  • Major refactoring and updating of Ant integration
  • Full release notes below

3.2.3 is a patch release with smaller bug fixes. Even if not explicitly listed in the changelogs below, anything in 3.2.3 will be in 3.3.0.

3.2.3 Change Log

  • [CORE-1919] – SpringLiquibase fails when dropFirst is true
  • [CORE-1987] – “mvn liquibase:diff” does not find any differences between databases
  • [CORE-1988] – Reported size for Oracle NVARCHAR2 columns is wrong
  • [CORE-1989] – Cannot set objectQuotingStrategy on root databaseChangeLog node
  • [CORE-2002] – AbstractResourceAccessor generates path in a unpredictable way
  • [CORE-2003] – Could not find implementation of liquibase.logging.Logger
  • [CORE-2042] – If liquibase.jar is nested in another jar/war/ear, it fails to start with a “cannot find implementation of liquibase.logging.Logger” error
  • [CORE-2058] – Load/Update tags should use “is null” not “= null” for null comparisons
  • [CORE-2070] – dropAllForeignKeyConstraints does not work on Firebird databases
  • [CORE-2075] – generateChangelog generates bad definition for TIME type
  • [CORE-2080] – Liquibase “empty” change not present in XSD version 3.2
  • [CORE-2065] – Use DOUBLE PRECISION for DOUBLE with Firebird
  • [CORE-54] – Support System Properties in Maven Plugin

 

3.3.0 Change Log

New Features

  • [CORE-16] – Support for “nonclustered” primary keys in mssql
  • [CORE-54] – Support System Properties in Maven Plugin
  • [CORE-1528] – Installer for Liquibase
  • [CORE-1598] – support for rename sequence
  • [CORE-1914] – New Change function: output
  • [CORE-1942] – Support for changeSet “labels”

Improvements

  • [CORE-549] – relativeToChangelogFile for loadData, loadUpdateData, sqlFile
  • [CORE-1438] – createView should support having the entire view definition in the change body
  • [CORE-1502] – CLONE – UpdateSQL needs to append a “/” to the end of createProcedure for Oracle
  • [CORE-1654] – logicalFilePath support in formatted sql
  • [CORE-1660] – “remarks” attribute is ignored in MSSQL
  • [CORE-1932] – support for encrypted passwords / custom properties
  • [CORE-1946] – Have a rpm package for liquibase (built with maven)
  • [CORE-1963] – Ability to define full CREATE VIEW statement in <createView> change.
  • [CORE-1990] – Preserve inline comments in view snapshots in mssql
  • [CORE-2060] – Support liquibase.properties files with unknown properties
  • [CORE-2061] – Improvements to Informix support
  • [CORE-2062] – Add onlyUpdate flag to loadUpdateData
  • [CORE-2064] – Use ignoreClassPathPrefix for rollback as well
  • [CORE-2065] – Use DOUBLE PRECISION for DOUBLE with Firebird
  • [CORE-2066] – Support for –outputFile in command line
  • [CORE-2067] – Refactor Ant Task codebase
  • [CORE-2068] – New liquibase.hostDescription property for additional details in the DATABASECHANGELOGLOCK table
  • [CORE-2069] – Use prepared statement in <update> change whenever a clob type is used
  • [CORE-2072] – Do not include Oracle internal tables in snapshot/diff

Bugs

  • [CORE-870] – Postgres, in an ALTER TABLE ALTER COLUMN statement, sometimes needs USING clause
  • [CORE-945] – Oracle : Temporary tables are created as regular tables
  • [CORE-1463] – Views not generated correctly with generateChangelog
  • [CORE-1556] – remarks attribute ignored for mysql
  • [CORE-1723] – unable to update on DB2/400, version V6R1, on jt400-6.7.jar
  • [CORE-1745] – afterColumn not working in MySQL
  • [CORE-1774] – Autocommit not restored on close in SpringLiquibase
  • [CORE-1882] – NullPointerException when MySQL foreign key points to an invalid table
  • [CORE-1919] – SpringLiquibase fails when dropFirst is true
  • [CORE-1922] – Sequence is not a reserved object name in HSQLDB
  • [CORE-1925] – liquibase scripts can not represent clustered indexes
  • [CORE-1937] – Oracle Float and VARCHAR precisions in changelog generated by generateChangeLog are incorrect
  • [CORE-1952] – liquibase loadData does not properly load numeric field in boolean always as false
  • [CORE-1956] – Double and float converted to FLOAT8(*, 17) and FLOAT4(*, 8) in PostgreSQL
  • [CORE-1958] – Column type of “TIMESTAMP(6)” under MySql converted to TIMESTAMP dropping fractional seconds
  • [CORE-1974] – dbchangelog-3.1.xsd missing <empty>
  • [CORE-1977] – CreateSequence with cacheSize=0 failing on Oracle
  • [CORE-1979] – MSSQL should not include parameters in SYSNAME data types
  • [CORE-1981] – Parameters set in included file are no longer set in 3.2.0
  • [CORE-1982] – Snapshot outputs defautlValueDate as defaultValueComputed on MSSQL for dates not in ISO format with a T in the middle
  • [CORE-1986] – includeAll from changeLogs within a jar is not working
  • [CORE-1988] – Reported size for Oracle NVARCHAR2 columns is wrong
  • [CORE-1993] – Drop table with cascade is not supported by Sybase
  • [CORE-1996] – addNotNullConstraint on h2 database has unexpected side effects
  • [CORE-1997] – Bit changelog default value of 1 executed as 0
  • [CORE-2002] – AbstractResourceAccessor generates path in a unpredictable way
  • [CORE-2010] – Oracle data type SDO_GEOMETRY snapshotted as SDO_GEOMETRY(1)
  • [CORE-2014] – applyToRollback property ignored when rollback changes are specified
  • [CORE-2015] – DiffChangeLog writes to the wrong point in the file on windows if file uses \n not \r\n
  • [CORE-2020] – Oracle default value current_timestamp converted to systimestamp
  • [CORE-2021] – Column remarks not snapshotted in mssql
  • [CORE-2026] – Oracle columns of type ANYDATA are snapshotted with a size
  • [CORE-2028] – generateChangeLog on SQL Anywhere 11.0.1 throws DatabaseException Driver Not Capable
  • [CORE-2032] – Snapshot incorrectly including clob/blob sizes on diff
  • [CORE-2051] – Not quoting VIEW params with spaces when snapshotting
  • [CORE-2054] – Add new “computed” column attribute to differentiate between an actual column name and a function as a column
  • [CORE-2063] – Fix for H2 autoincrement “start with” and “increment by” syntax
  • [CORE-2070] – dropAllForeignKeyConstraints does not work on Firebird databases
  • [CORE-2075] – generateChangelog generates bad definition for TIME type
  • [CORE-2080] – Liquibase “empty” change not present in XSD version 3.2
  • [CORE-2081] – PrimaryKeyExists precondition without tableName is broken
  • [CORE-2082] – Column snapshot on PostgreSQL does not include precision information for numeric data type
  • [CORE-2087] – Executing against Oracle doesn’t respect liquibaseSchemaName or liquibaseCatalogName
  • [CORE-2088] – outputDefaultSchema and outputDefaultCatalog command line parameters not respected
  • [CORE-2093] – Error: Property ‘relativeToChangelogFile’ not found on object type liquibase.change.core.LoadDataChange
  • [CORE-2094] – Liquibase.dropAll() should reset the lock service
  • [CORE-2095] – Invalid generated changeset for mysql bit with defaultValue 0

Contexts vs. Labels

$
0
0

A new feature with Liquibase 3.3 is “labels”. Labels are similar to contexts in that both allow you to chose a subset of changeSets to execute at runtime. Labels are also similar to contexts in that both are purposely vague terms because they are fairly generic features can enable many different use cases. Where they differ is in who has the power to specify complex logic: the changeSet author or the deployment manager.

Contexts

Contexts in Liquiase have been available for quite a while, and they started out primarily as a way of “tagging” changeSets so they can be chosen at runtime. One common use is to mark changeSets that insert test data as context=”test” so that in your development and QA environments you you can run liquibase with –contexts=test to get the test data and in production you run with –contexts=prod to not have test data. Contexts are also helpful for marking changeSets based on feature sets to include (context=”shoppingCart”) or bundle (context=”pro”) or even customer (context=”acme_inc”). For complex cases, multiple contexts can be applied to a changeSet such as context=”acme_inc, pro” and multiple contexs can be chosen at runtime such as –contexts=free,qa.

With Liquibase 3.2, support was added to for context expressions in changeSets. Now, when you are defining your changeSet you can specify complex logic such as context=”!test” or context=”qa or (acme_inc and dev)”. The context logic can only be specified in your changeSet definition, however. When running Liquibase, you can still specify multiple contexts, but you are just listing out all the contexts that apply to the current Liquibase run.

Labels

Labels were added in Liquibase 3.3 to work like contexts, but “backwards” in who can specify logical expressions. In your changeSet you can only specify a simple list of “labels” that apply to the changeSet but at runtime you can write a complex expression to chose the labels you want to execute. This allows you to specify a changeSet with labels=”qa, acme_inc” and then at runtime use expressions such as –labels=”!acme_inc” or –labels=”pro or (free and beta)”.

Which is right for you? 

Whether you should use contexts or labels comes down to whether the changeSet writer or the Liquibase executor best understands and/or needs the most control over which changeSets to execute.

  • If the changeSet author needs to be able to specify complex logic based on the kind of environment that Liquibase will run in, use contexts.
  • If the person executing Liquibase needs to specify complex logic to chose changeSets to run, use labels.
  • If you do not use complex expressions, there is no functional difference between them.

Remember: you can use both.

Example Use Cases

Contexts work best when you can simply enumerate/describe features of the runtime environment:

  • Free vs Pro versions
  • QA vs. Dev. vs. Prod environments
  • Customer A vs. Customer B

Labels work best when you can simply enumerate/describe what a changeSet is for, but the deployment time environment is complex to describe. An example of when labels would work well is when you can describe changeSets as for a particular feature or version such as “1.0″ and/or “shopping_cart” but the decision on which features and/or versions needs to run is complex and chosen at deployment time. Labels in this case would allow you to run with –labels=”1.0 or (1.1 and shopping_cart)” to deploy the 1.0 changeSets and only the 1.1. features related to the shopping cart to one database and –labels=”1.0 or (1.1 and !shopping_cart)” to another database.

When in doubt, I usually go with contexts because that will simplify deployment configuration (to minimize release-day problems) while giving changeSet authors the option to handle complex logic if needed.

 

Liquibase 3.3.2 Released

$
0
0

Liquibase 3.3.2 is officially released. It is primarily a bugfix release, but has one major new feature: object diffChangeLog/generateChangeLog object filtering.

includeObjects/excludeObjects logic

You can now set an includeObjects or excludeObjects paramter on the command line or Ant. For maven, the parameteres are diffExcludeObjects  and diffIncludeObjects. The format for these parameters are:

  • An object name (actually a regexp) will match any object whose name matches the regexp.
  • A type:name syntax that matches the regexp name for objects of the given type
  • If you want multiple expressions, comma separate them
  • The type:name logic will be applied to the tables containing columns, indexes, etc.

NOTE: name comparison is case sensitive. If you want insensitive logic, use the `(?i)` regexp flag.

Example Filters:

  • “table_name” will match a table called “table_name” but not “other_table” or “TABLE_NAME”
  • “(i?)table_name” will match a table called “table_name” and “TABLE_NAME”
  • “table_name” will match all columns in the table table_name
  • “table:table_name” will match a table called table_name but not a column named table_name
  • “table:table_name, column:*._lock” will match a table called table_name and all columns that end with “_lock”

Full 3.3.2 Change Log:

  • [CORE-875] – Ignore tables for diffs and generateChangelog
  • [CORE-1877] – SQLOutput prints endDelimiter regexes
  • [CORE-2114] – AddAutoIncrement on Postgres does not work when changes are applied on a specific schema
  • [CORE-2141] – handling dependencies and WAR as classpath
  • [CORE-2166] – SpringLiquibase: includeAll within jar causes SetupException
  • [CORE-2172] – dropPrimaryKey without constraint name on sql server doesn’t honour schema information
  • [CORE-2174] – Bad exception handling in OracleDatabase.setConnection
  • [CORE-2180] – NPE with bad name
  • [CORE-2182] – ClassLoader leak due to shutdown hooks

Since the 3.3.0 announcement, 3.3.1 was also released in December as a bugfix release with the following changes:

  • [CORE-1920] – SpringLiqubase includeAll is not including files
  • [CORE-2009] – ClassCastException when executing a custom task change (AntClassLoader problem)
  • [CORE-2097] – “mvn liquibase:futureRollbackSQL” asks for tag, count or date
  • [CORE-2099] – SQLAnywhere support (Driver not capable)
  • [CORE-2103] – changelogSchemaName/changelogCatalogName configuration options will not work on Oracle DB
  • [CORE-2104] – ConcurrentModificationException iterating over System.getProperties().entrySet()
  • [CORE-2105] – Maven profile performing dropAll and update on Oracle failing with an error on populated database.
  • [CORE-2107] – LOWER() keyword fails on Postgres createIndex task
  • [CORE-2108] – dropAll command trying to drop column on table that has already been dropped
  • [CORE-2116] – Could not find implementation of liquibase.logging.Logger
  • [CORE-2118] – Change default diffChangeLog/generateChangeLog objectQuotingStrategy back to LEGACY
  • [CORE-2119] – Bad finally block in SpringLiquibase.afterPropertiesSet()
  • [CORE-2120] – LoadUpdateData with value=NUMERIC quoting values
  • [CORE-2121] – DB2: DiffChangeLog/GenerateChangeLog/DropAll sees alias column and tries to drop/add them
  • [CORE-2127] – updateSQL creates duplicate DATABASECHANGELOGLOCK tables
  • [CORE-2130] – setFetchSize to a negative value breaks Oracle JDBC Driver
  • [CORE-2134] – ExecuteCommand won’t run with no os attribute.
  • [CORE-2136] – Mysql must quote PARTITION as a keyword
  • [CORE-2137] – Special characters (&#13;) copied during generateChangelog on DB2/400
  • [CORE-2139] – H2Database.supportsDropTableCascadeConstraints() returns false
  • [CORE-2142] – generateChangeLog not including all columns in a table
  • [CORE-2146] – snakeyaml is pulled in as transitive dependency for using projects
  • [CORE-2149] – Liquibase command line fails
  • [CORE-2150] – On the 3.3.0-SNAPSHOT, liquibase –version returns 3.2.0
  • [CORE-2153] – Liquibase 3.2.1 is no longer compatible with Oracle 9
  • [CORE-2155] – diffTypes=data fails with java.sql.SQLException: Attribute value not valid (dataOutputDirectory attribute causes build to fail)
  • [CORE-2156] – Resource loader can’t load changelog file
  • [CORE-2157] – SQLException if there are single quotes in ChangeSet
  • [CORE-2159] – Datetime2 no longer used for MSSQL
  • [CORE-2161] – includeAll relativeToChangelogFile=”true” doesn’t work
  • [CORE-2164] – SpringLiquibase: includeAll within jar causes NullPointerException
  • [CORE-2179] – Creating functional indexes
  • [CORE-2115] – Really slow when using fat jars
  • [CORE-2125] – Make DatabaseChangeLog#include(String, boolean, ResourceAccessor) public
  • [CORE-2148] – Build failure on jdk-1.8
  • [CORE-2152] – Change logs in json format not processed by liquibase – parsing errors

 Updated Exensions

The following extensions have also been recently updated with bugfixes, new features and support for Liquibase 3.3.x

Download

As always, Liquibase can be downloaded from the Liquibase download page and is available in the Maven repository as org.liquibase/liquibase-core. The extensions can be downloaded from their corresponding github repository “Release” pages.

Liquibase 3.3.3 Released

$
0
0

Liquibase 3.3.3 is primarily a bugfix release

As always, Liquibase can be downloaded from the Liquibase download page and is available in the Maven repository as org.liquibase/liquibase-core.

Fixed Issues:

  • [CORE-1768] – Oracle dropAll fails on spatial tables and sequences
  • [CORE-1840] – Liquibase fails when run on a computer that can’t connect to the internet
  • [CORE-1857] – Wrong column size detection on varchar2 fields with char as datatype
  • [CORE-1866] – Filtering changelog list by includeAll tag is not working
  • [CORE-1943] – Handle Error: InetAddress.getLocalHost().getHostName() UnknownHostException results in NoClassDefFoundError
  • [CORE-1958] – Column type of “TIMESTAMP(6)” under MySql converted to TIMESTAMP dropping fractional seconds
  • [CORE-1967] – includeAll uses full file path for sql changelogs
  • [CORE-2023] – Problem using includeAll with SpringLiquibase
  • [CORE-2126] – Postgres 9.3 – Drop table With Cascade – Not Supported
  • [CORE-2156] – Resource loader can’t load changelog file
  • [CORE-2186] – AbstractResourceAccessor#convertToPath(String, String) fails for processing includeAll from Classpath
  • [CORE-2192] – NoSuchMethodException when generating offline Oracle migration script
  • [CORE-2199] – Liquibase adds a semicolon after a stored proc definition making the stored proc unusable
  • [CORE-2202] – liquibase.should.run inverted boolean
  • [CORE-2204] – valueNumeric not being set when using prepared statements
  • [CORE-2206] – diffChangeLog with JPA-annotated entities causes ConcurrentModificationException
  • [CORE-2208] – Typo in message
  • [CORE-2210] – java.lang.NullPointerException when file is empty
  • [CORE-2214] – When inserting string value starting and ending with apostrophes (quotes) the value is not quoted in the generated SQL
  • [CORE-2218] – Regression on modifyDataType : VARCHAR2 was supported on 3.2…and fails on 3.3
  • [CORE-2239] – Remarks attribute in renameColumn causes parse error
  • [CORE-2240] – setDropFirst(true) still broken on empty database
  • [CORE-2262] – 3.3.2 ant task dies on NPE in ChangeLogParameters
  • [CORE-2263] – Index Snapshot – doesn’t include upper cased name indexes when db is NOT case sensitive
  • [CORE-2274] – Ant Upade Task does not consider changeLogFile correctly if it is contained in a JAR
  • [CORE-2279] – Rollback fails in MS SQL 2008 using liquibase 3.3.2
  • [CORE-2284] – Creating a DatabaseChangeLog() results in NPE
  • [CORE-2290] – Liquibase gives different results from Ant and the command line
  • [CORE-2301] – Regression from 3.2.3 in mssql 2000 unsupported usage of varchar(max) and sys.extenden_properties
  • [CORE-2304] – Autoincrement on type INT4 fails
  • [CORE-2310] – IncludeAll Fails with Unknown Reason Error
  • [CORE-2315] – NPE in CommandlineResourceAccessor
  • [CORE-2325] – Liquibase – New versions break DB create
  • [CORE-2329] – Escaped reserved keywords in HSQL are stored in lower case instead of upper case.
  • [CORE-2330] – includeAll uses full file path with includeAll
  • [CORE-2261] – UpdateSQL needs to append a “/” to the end of createProcedure for Oracle
  • [CORE-2287] – Improve support for Groovy-based tests in Eclipse
  • [CORE-2296] – Upgrade Groovy and Spock to maintained versions
  • [CORE-2318] – Add support for converting BigDecimal objects to a SQL string via DataTypeFactory

Liquibase and Datical

$
0
0

For those of you who don’t know, I’ve been working for Datical for the last year and a half as “Benevolent Dictator for Life.” I often joke that I feel like I’m retired because after years of working on Liquibase as a hobby on nights and weekends I’m now spending my days working on my “hobby”.

The rest of the Datical team works on Datical DB which uses Liquibase. Datical DB wraps additional functionality around Liquibase such as:

  • Database specific objects such as Oracle Packages & SQL Server Functions
  • Forecast to simulate deployments
  • HTML reporting
  • Out-of-the-box integrations with Jenkins, IBM UrbanCode, CA Release Automation, and others
  • Rules Engine (using Drools) to help you control specific objects in your deployments.

I’ve always considered the scope of Liquibase to be “git for your database”–flexible but with a single, vendor neutral purpose that is part of an overall application deployment process. Datical DB builds out the rest of toolset for those who need it.

To help highlight and differentiate Liquibase and Datical, I’ve made a few updates to liquibase.org including a feature comparison on the download page and a more descriptive “Enterprise Version” menu link. Liquibase continues to be a separate product from Datical and will always remain open source. The changes are simply to point people to Datical DB if they are interested without getting in the way of those who simply use Liquibase.

Liquibase 3.3.4 Released

$
0
0

Bugfix release Liquibase 3.3.4 is now available from liquibase.org/download and through the Maven repositories.

The most notable fix was to make the Maven plugin “liquibase.should.run” flag default back to true like it should have vs. “false” in 3.3.3.

Full changelog:

  • [CORE-2360] – Maven – Skip is active by default
  • [CORE-2199] – Liquibase adds a semicolon after a stored proc definition making the stored proc unusable
  • [CORE-2344] – Unknown host exception on OS RHEL 6.5
  • [CORE-2346] – IncludeAll does not work when runing liquibase from inside a jar
  • [CORE-2357] – alterSequence does not work as expected when you need to change the cache size
  • [CORE-2366] – Derby Network server works with command line but not with maven “Liquibase skipped due to maven configuration”
  • [CORE-2368] – No SQL outputted for <update> change

Liquibase 3.3.5 Released

$
0
0

Yes, I know 3.3.4 just came out yesterday, but there was a configuration issue that affected dependencies in Maven.

3.3.5 resolves this with no other changes.


Trimming Liquibase ChangeLogs

$
0
0

For people who have used Liquibase for a long time, a common question they have is how to clear out a changelog file that has gotten unwieldy.

The standard process for using Liquibase is to append individual change sets to your changelog file for each database change you need to make. Over time those changes can build up to thousands of entries, many of which are now redundant (create a table and later drop it) or inefficient (create a table, then add columns individually vs. just creating the table with all the columns). What is the best way to simplify all that cruft that has built up?

My first response is always “Do you really need to simplify it?” You built up that changelog over a long period of time and you have ran it and tested it countless times. Once you start messing with the changelog file you are introducing risk which has a cost of its own. Does whatever performance or file size concerns you have really outweigh the risk of messing with a script that you know works?

If it is worth the risk, why is it work the risk? Sometimes the problem is that your changelog file has just gotten so large that your editor chokes on it, or you get too many merge conflicts. The best way to handle this is to simply break up your changelog file into multiple files. Instead of having a single changelog.xml file with everything in it, create a master.changelog.xml file which uses the <include> tag to reference other changelog files.

<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog/3.3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/3.3
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.3.xsd">
<include file="com/example/news/news.changelog.xml"/>
<include file="com/example/directory/directory.changelog.xml"/>
</databaseChangeLog>

When you run `liquibase update` against the master.changelog.xml file, changeSets in com/example/news/news.changelog.xml will run and then the changeSets in com/example/directory/directory.changelog.xml will run. You can break up changeSets in whatever manner works best for you. Some break them up by feature, some break them up by release. Find what works best for you.

Other times, the problem is that `liquibase update` is taking too long. Liquibase tries to be as efficient as possible when comparing the contents of the DATBASECHANGELOG table with the current changelog file and even if there are thousands of already ran changeSets, an “update” command should take just seconds to run. If you are finding that update is taking longer than it should, watch the Liquibase log to determine why. Perhaps there is an old runAlways=”true” changeSet that no longer needs to run or there are preconditions which are no longer needed. Running Liquibase with –logLevel=INFO or even –logLevel=DEBUG can give additional output which can help you determine which changeSets are slow. Once you know what is slowing down your update, try to alter just those changeSets rather than throwing out the whole changelog and starting from scratch. You will still want to retest your changelog in-depth, but it is a far less risky change.

For other people, they find that `liquibase update` works well for incremental updates, but creating a database from scratch takes far too long. Again I would ask “is that really a problem?” Are you re-creating databases often enough that the risk of a change to the creation script makes sense? If you are, your first step should be to look for problem changeSets as described above. Databases are fast, especially when they are empty. Even if you create a table only to drop it again that is usually just a few milliseconds of overhead and not worth optimizing. The biggest performance bottlenecks in creating a database are usually indexes, so start with them. If you are creating and updating indexes frequently in your creation process, you may be able to combine those changeSets into something more efficient.

When you need to surgically alter your existing changeSets, remember how Liquibase works: each changeSet has an “id”, an “author”, and a file path which together uniquely identifies it. If the DATABASECHANGELOG table has an entry for that changeSet it will not run it. If it has an entry, it throws an error if the checksum for the changeSet in the file doesn’t match what was stored on the last run.

How you modify your existing changeSets will also depend on your environment and where in the changelog the problem changeSets are. If you are modifying changeSets that have been applied to all of your environments and are now only used on fresh database builds you can treat them differently than if they have been applied to some databases but not yet to others.

To merge or modify existing changeSets you will be doing a combination of editing existing changeSets, removing old changeSets, and creating new ones.

Removing unneeded changeSets is easy because Liquibase doesn’t care about DATABASECHANGELOG rows with no corresponding changeSets. Just delete out of date changeSets and you are done. For example, if you have a changeSet that creates the table “cart” and then another that drops it, just remove both changeSets from the file. You must make sure, however, that there are no changeSets between the create and the delete that make use of that table or they will fail on a fresh database build. That is an example of how you are introducing risk when changing your changelog file.

Suppose instead you have a “cart” table that is created in one changeSet, then a “promo_code” column is created in another and an “abandoned” flag is created in another.
<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-3.3.xsd">
<changeSet author="nvoxland" id="1">
<createTable tableName="cart">
<column name="id" type="int"/>
</createTable>
</changeSet>

<changeSet author="nvoxland" id="2">
<addColumn tableName="cart">
<column name="promo_code" type="varchar(10)"/>
</addColumn>
</changeSet>

<changeSet author="nvoxland" id="3">
<addColumn tableName="cart">
<column name="abandoned" type="boolean"/>
</addColumn>
</changeSet>

</databaseChangeLog>
One option would be to combine everything into a new changeSet using the existing id=”1” and delete the other changeSets.

<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-3.3.xsd">
<changeSet author="nvoxland" id="1">
<validCheckSum>7:f24b25ba0fea451728ffbade634f791d</validCheckSum>
<createTable tableName="cart">
<column name="id" type="int"/>
<column name="promo_code" type="varchar(10)"/>
<column name="abandoned" type="boolean"/>
</createTable>
</changeSet>
</databaseChangeLog>

This will work well if all existing databases have the cart table with the promo_code and abandoned columns already added. Running Liquibase against existing databases just sees that id=”1” already ran and doesn’t do anything new. Running Liquibase against a blank database will create the cart table with all the columns right away. Notice that we had to add the flag or existing databases will thow an error saying that id=”1” has changed since it was run. Just use the checksum in the error message in the validCheckSum tag to mark that you know it changed and the new value is OK.

If you have some databases where the promo_code and/or abandoned columns have not yet been added, update the original createTable as before, but use preconditions with onFail=”MARK_RAN” to handle cases where the old changeSet ran while still not adding the columns again if the new changeSet ran.

<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-3.3.xsd">
<changeSet author="nvoxland" id="1">
<validCheckSum>7:f24b25ba0fea451728ffbade634f791d</validCheckSum>
<createTable tableName="cart">
<column name="id" type="int"/>
<column name="promo_code" type="varchar(10)"/>
<column name="abandoned" type="boolean"/>
</createTable>
</changeSet>

<changeSet author="nvoxland" id="2">
<preConditions onFail="MARK_RAN">
<not><columnExists tableName="cart" columnName="promo_code"/></not>
</preConditions>
<addColumn tableName="cart">
<column name="promo_code" type="varchar(10)"/>
</addColumn>
</changeSet>

<changeSet author="nvoxland" id="3">
<preConditions onFail="MARK_RAN">
<not><columnExists tableName="cart" columnName="abandoned"/></not>
</preConditions>
<addColumn tableName="cart">
<column name="abandoned" type="boolean"/>
</addColumn>
</changeSet>

</databaseChangeLog>

Now, on existing databases that have all 3 changeSets already ran, Liquibase will just continue on as before. For existing databases that have the old cart definition, it will see that the columns don’t exist for id=”2” and id=”3” and execute then as usual. For blank databases, it will create the table with the promo_code and abandoned columns and then in id=”2” and id=”3” it will see that they are already there and mark that they have ran without re-adding the columns. A word of warning, however: using preconditions will add a performance overhead to your update executions and are ignored in updateSQL mode because Liquibase cannot know how applicable they are when changeSets have not actually executed. For that reason it is best to avoid them if possible, but definitely use them when needed. Preconditions also add complexity to your changelog which will require additional testing so keep that in mind when deciding whether to modify your changelog logic. Sometimes it is easiest and safest to wait until all your databases have the columns and then modify the changeSets to avoid the preconditions.

The cart/promo_code/abandoned example shows some basic patterns you can use when modifying existing changeSets. Similar patters can be used to optimize whatever your bottlenecks are. Just remember when you change one changeSet, it can affect other changeSets below which may need to be modified as well. This can easily spider out of control so be mindful of what you are doing.

If you end up finding that it will work best to completely restart your changelog, see http://www.liquibase.org/documentation/existing_project.html which describes how to add Liquibase to an existing project (even if that project was previously managed by Liquibase).

(Originally posted to DZone)

Liquibase Downloads Moved Off SourceForge

$
0
0

When I first released Liquibase as open source in 2006, Sourceforge was the obvious place to host the project. Over the years, however, I’ve been slowing shifting functionality off sourceforge to liquibase.jira.com, github.com/liquibase and this blog.

For the last few years, the only thing remaining on SourceForge was the download file hosting. Given the recent issues SourceForge has had with GIMP, Nmap, and others it is time to move the file downloads off as well. Users entrust Liquibase with their most valuable asset–their data. I want to ensure that trust is sound which is why I am moving the download source.

What impact does this have? Very little. The main Liquibase download page is still http://liquibase.org/download and there have been no changes to that page except where the links point. If you were one of the few people who went to the sourceforge project page to download the files, they are no longer available and you must now go to the liquibase.org/download page instead.

Welcome to the Liquibase Blog

$
0
0

We are starting the Liquibase blog for several reasons:

Partly, it will be a place to announce new features and releases of Liquibase.

Primarily, however, it will be a place to comment on agile database development in general, best practices, and pitfalls to avoid.

The Road to Liquibase 1.0 and Beyond

$
0
0

There is a lot going on in Liquibase-land right now.

We are finishing up code and documentation clean-up of the Liquibase migrator and hope to have an initial release candidate available by the middle of June.

As long as no major bugs are found, we expect 1.0 to be released by the end of June.

Concurrent to the final code reviews and bug fixes leading up to 1.0, we have started on a SQuirreL plug-in for automatic creation of changelog files. There is no target date yet for an initial release, but it will probably be post 1.0. If there is anyone interested in working on a plug-in for other IDEs, just let us know.

We are also already looking at features to include in a 1.1 release. Items currently on the top of the list include an additional changeSet attribute for specifying the types of databases that it should be run against as well as support for additional databases (Sybase, DB2, Derby, perhaps more).

Unit Testing the Database Access Layer

$
0
0

The Problem

Writing unit tests for code that accesses a database is a difficult problem, and it’s one I’ve struggled with for several years. The problems come down to a balancing act between several (often competing) requirements:

  1. The unit test must not not break due to changes outside the class under test.
  2. The unit test must not break due to code refactoring that doesn’t break functionality.
  3. The unit test must run very fast.
  4. The unit test must remain valid through database refactorings.
  5. The unit test must fail if the code doesn’t function correctly.

I’ve tried several approaches in the past and was unsatisfied with them:

Mock the Database Connection

This is the most “pure” unit testing approach because it completely isolates the class under test from all external dependencies.

Connection conn = createMock(Connection.class);
Statement statement = createMock(Statement.class);
expect(conn.createStatement()).andReturn(statement);
expect(statement.execute("SELECT ID FROM PERSON;")).andStubReturn(true);
statement.close();
expectLastCall();
replay(conn);
replay(statement);

ClassUnderTest classUnderTest = new ClassUnderTest ();
classUnderTest .findPersonIds();

verify(conn);
verify(statement);

The advantages to mocking the database connection is that the tests run fast and don’t change to due to creating of other unit tests.

In practice, however, I’ve found these tests to be more or less worthless. They (like most mock tests) really just test the implementation of the method, not the behavior of the method, and therefore often fail due to code refactoring. The biggest problem, however, is that the access to the database is really the whole point of the method and there are so many ways that can fail.

I would argue, that really the method is implemented as SQL with a Java wrapper around it and in the mock unit test, only the Java code is really tested–the SQL is often just cut and pasted from what is in the method.

Throw in changes to the database over the life of the project (the “person” table name changes to “employee”) and you end up with tests that pass, but don’t really tell you if your code will work.

Create Test Data Sets For Each Test

The obvious response to the problems with mock database tests is to have your tests access an actual database. This method will create tests that actually fail if your SQL is wrong or your database schema changes without a corresponding code update, but brings with it its own difficulties, primarily with keeping the database in a known state. There are tools available such as DbUnit and Unitils to help with the test-data management, but these have disadvantages.

The main problem is that the way the test data is stored, it is very dependent on the exact schema when it was created, and if your database structure changes your test data can no longer be inserted and your tests are now worthless. For example, if your test is created with test data that inserts rows into a “person” table, but later that table is renamed to “employee”, your insert statements will no longer execute. Depending on the schema changes, you may be able to recover your test data with a search and replace, but often the changes are too much and it has been long enough since you wrote the test that you don’t remember exactly what was supposed to be in the data.

Additionally, the data is inserted as part of the setup method for each test. Accessing databases in Unit tests slows them down considerably already, we should work to minimize any calls to the database that we have to do.

Manage Test Data With All Other Database Changes

To solve the problem of keeping test data definitions from getting out of sync with the schema, you need to have your test data built up along with your database so it will be modified by database refactoring that were made after it was initially created. For example, here is a database change script:

  1. create table person
  2. insert row into person test data
  3. insert row into person test data
  4. rename table person to employee

By including the test data with your database changes, the data is automatically kept up in the same way the production data would be. Using this technique over the dataset per method also has the advantage of performing better because the data is only inserted once, but it has the disadvantage that you need to handle all the test data that any method would want in one place. To facilitate this technique, I built the idea of execution contexts into Liquibase so you can mark the test data changes and only apply them in environments where you run unit tests. So far I have been happy with the results. The tests fail when there is a differences between the database schema and what the code is expecting or when there is a bug in my SQL and I haven’t lost any tests due to database refactorings.

The tests don’t run as fast as mock connection tests would, but they aren’t prohibitively slow. We have over 1000 unit tests in one project that all run in about a minute. It would be better if they would all run in 10 seconds, but what good are fast tests that don’t actually test what’s important? Just remember to mock the (already tested) data access layer in higher layers of your code so you don’t take the database performance hit any more than you need to.

With this technique, you need to always remember that your test data set will evolve over time and write your tests accordingly, but how to do that will be covered in a later blog posting.

Building Database Tests that Don't Break

$
0
0

As I discussed in Unit Testing the Database, a difficulty you run into when using a shared dataset for unit tests is making sure you write your test in such a way that changes to the dataset will not break older tests.

Note: depending on the database access framework you use, you will actually access the database through a Connection, EntityManager, Session, PersistenceManager etc. I’ll use the term “Connection” generically for any of these access types because the same pattern applies to them all.

Don’t Rely on Particular Data

The following is an example of a bad test that will break when the dataset changes:

public void testFindActive() {
Collection returnSet = new PersonDAO(connection).findActive();
assertEquals(5, returnSet.size());
}

When you first write the above test, it will work fine because you have know your data set has 5 person rows in it. However, when you get to the next test and realize you need more test data and add a new person row, the testFindActive() method will suddenly fail when the method being tested isn’t actually broken.

What I have found to be the best way of writing better tests is to create a framework that lets you describe what type of rows you expect your method to return. Here is a code example:

public void testFindActive() {
    assertDataCorrect("Did not return active people correctly",
        new DataComparator(entityManager, Person.class) {
            public boolean include(Person person)  {
                    return person.isActive();
            }
        }
}

There are two advantages to this method of testing:

  1. It doesn’t break if person rows are added or removed
  2. The test better documents the intent of the isActive() method
  3. Since you are describing your filter via normal object methods, the test will stay up to date as you refactor your code/

Of course, you need to have an implementation of assertDataCorrect(). Unfortunately, although it is fairly straightforward to implement, how you actually implement it varies depending on your database access framework. Here is a pseudo-code sample to get you started:

public abstract class DataTestCase extends TestCase {
    assertDataCorrect(String message,
            DataComparator comparator, Collection
            collectionToTest) {
        assertNotNull("Collection to test was null, poor test", collectionToTest);
        assertTrue("Collection to test was empty, poor test", collectionToTest.size() > 1);

        List allObjects = comparator.getEntityManager
            .createQuery("from " + comparator.getEntityClass().getName());

        int matchingObjects = 0;
        List filteredObjectsFromDB = filterObjects(comparator, allObjects);
        for (Object objectFromDB : filteredObjectsFromDB) {
          matchingObjects++;
          if (!collectionToTest.contains(object)) {
                fail ("Expected object '"+object.toString()+"' not found in collection");
          }
        }
        assertTrue("Expected "+matchingObjects
                +" objects, passed collection was "+collectionToTest.size(),
                matchingObjects == collectionToTest.size());
    }
}

Some notes on the above implementation:

  1. The class extends TestCase so as long as all your database tests extends the new DatabaseTestCase you will always have access to the new assertions.
  2. If the passed collection is null or empty, the test fails. The reason for this is because if your query is returning nothing from the database, you don’t really know if it is filtered down correctly. It could be working correctly, or it could be you have a typo in your filter that will always make it return no results.
  3. The assert function selects out all rows from the database and compares them to the returned objects. If you have a lot of rows, this will be a very poor-performing test. In practice, your test dataset will normally not grow so large this will become a problem, but it is something to keep in mind if you ever think about running your test suite against a copy of production data.

Eventually, I hope to create a database unit testing library that better encapsulates this logic, but have not had the time yet. If there is anyone who would like to volunteer to help out with creating one, please contact me at nathan@voxland.net.

Start a transaction in your setUp() method and roll it back in your tearDown() method

By running your test in a transaction, you ensure that (normally) whatever your code under test does to the database will have no lasting affect and will not cause later tests to fail. This is a simple extension of the unit testing principal of not assuming your tests will run in a particular order and to have no side effect of your tests.

If your code under test attempts to start and commit transactions, you may need to create a wrapper connection around the actual connection that intercepts calls to start and end transactions and simply logs that they happened. That way you can test that the commit you expected actually happened and still actually roll back the database in your tearDown() method.

The thing to watch out for is auto-committing code in your class under test. For example, if your tested logic creates a new table, many databases will auto-commit the transaction. In these cases, you will need to manually undo the changes in a finally block in your test so you still follow the “no side-effect” rule.

Liquibase 1.0 RC1 Released

$
0
0

Liquibase 1.0 RC1 has been released. Changes since 0.9.1 are primarily code refactoring to make sure it is a clean code base to work with post-1.0. The only new feature is a batch and shell script for easier running of the command line migrator. There were also the usual documentation improvements and bug fixes.

Please report all bugs you find with the release candidate so they can be addressed in the 1.0 release, particularly in the Maven support. Unless there are show-stopper bugs found, the final 1.0 release should be out in two weeks or less.


Liquibase 1.0 RC2 Released

$
0
0

Liquibase 1.0 RC2 has been released. Changes since RC2 are primarily minor bug fixes.

The reason for having a RC2 rather than 1.0 final is that we changed exceptions thrown by some methods and want to make sure that change does not cause any unexpected problems.

Please report all bugs you find with the release candidate so they can be addressed in the 1.0 release, particularly in the Maven support.

Unless there are show-stopper bugs found, the final 1.0 release should be out next week.

The Problem With Database Diffs

$
0
0

When talking about how Liquibase works, I’m often asked “Why not just do a database diff to know what changes need to be applied to a new database?”

There are advantages to the database diff technique compared to Liquibase’s method of tracking changes, mainly related to not relying on developers to remember to document the changes they made. You simply have everyone make whatever changes they want to the dev database and let the diff tool sort it out.

There are two fundamental flaw with diff tools, however. The first is is that while they do a good job of showing what the differences are syntactically, they don’t understand the semantics of the changes. For example, if you rename a column from “fname” to “firstname”, a diff will show you that there is a “fname” column that needs to be dropped and a “firstname” column that needs to be be added. If you follow these suggestions, you will end up with a database that is structured correctly, but you will loose your valuable data when the changes is applied to an existing production system. You need to not just understand what the structural differences are, you also need to understand why they are.

The second flaw is that it is impossible to diff the data so you find inserts, updates, and deletes that must be applied for the system to work correctly. These data changes can range from additional lookup table values to copying data in a de-normalizing process and can be just as important as the structural changes. Theoretically, a diff tool could also check for new, updated, and missing data between database, but in practice this cannot work for two reasons:

  1. Performance. As your data set grows, the amount of information to compare grows until it is unmanageable.
  2. Changing Data. During development, test data is often added to the development database that shouldn’t be copied into other databases. Also, new data may be added to testing and production databases that should not be deleted just because it doesn’t exist in the development database.

If you track your data changes along with your structural changes during development, however, you can be certain that they will be applied correctly down the road.

In the end, I feel that the problems associated with a diff tool outweighs the convenience and simplicity they offer. While they can have great value as a “did all the changes get stored in the change log correctly” safeguard, it should not be relied apon as your primary means of tracking database changes.

Managing Change Logs

$
0
0

While you can include all your change sets in one giant change log, there are many good reasons to split them up. While there are many ways to divide your change logs, the best strategy I have found is to create a change log per major java package that contains data access code. This approach has several advantages:

Easy to Know Where to Add (and Look For) Database Changes</b>

If you are making a database change due to code in a package, you know exactly where to put the change.

Makes Code-Reuse and Code-Repackaging Easier

Since packages are often used as a logical code unit, the code to manage database changes can be moved and re-used along with the java code.

Keeps Database Changes Closer to the Code

You don’t need to go hunt through your file navigator as you switch between adding required databases changes and making the corresponding code changes.

Fewer File Conflicts

On multi-developer teams, the database change logs are a shared resource with many individuals editing them. Like any version controlled file, the more developers and the more branches that touch a file, the more chances for problems. Breaking up a change log into multiple files limits the changes and extent of merge issues.

There are, of course, other ways to break up your change log files including one change log per project and one change log per table. Depending on your requirements, these or other strategies may work better for you. The important thing is to find what works best for you.

Liquibase 1.0 Released

$
0
0

The Liquibase team is proud to announce version 1.0. Liquibase is an open source (LGPL) java-based tool for managing database changes and refactorings. It has been under active development for over a year and supports many features including:

  • Change tracking format that supports multiple developers and code branches
  • Thirty built-in refactorings including “Merge Columns” and “Add Lookup Table”
  • Can execute updates directly, or save SQL for review by DBAs
  • Can roll back databases to earlier versions based on dates, tags, or number of changes
  • Database independent. Currently supports MySQL, PostgreSQL, Oracle, and MSSQL with additional databases planned for version 1.1.
  • Can be executed as an Ant task, a Maven Plug-in, as a Servlet Listener, or though a command-line program
  • Changes can be tagged with “contexts” so not all changes need to be applied to all environments
  • Uses a distributed locking system to protect against machines upgrading the same database at the same time
  • Extensive documentation including a quick-start guide and manual

As a database change tracking tool, Liquibase is useful for any project with a database, but is especially useful in an agile environment due to the large number of changes that are generated throughout the project’s lifecycle.

There are many post-1.0 features planned, including support for additional databases (DB2, Sybase, Derby and HSQL are already implemented in the 1.1 branch), a database-refactoring IDE plug-ins, additional refactorings, a database diff tool, and more.

We would like to thank everyone who helped us get to the point we are at today.

The Problem With Rails Active Migrations

$
0
0

Rails Active Migration is nice for its simplicity, but in non-trivial projects, it quickly falls apart due to limitations it has regarding multiple developers and/or branches. It’s a well known problem

The fundamental problem is that Rails tracks the “database version” as a single incrementing integer. That works fine when only one developer is adding migrations and when there is only one branch. When you add developers and branches, however, you quickly run into problems with duplicated version numbers, and missed migrations because the production “database version” is higher than a newly merged in migration.

The reason this is a problem is because there the reliance on a the “database version” concept. While there may be a logical database version that you can think about, really the state of the database is simply the set of all the applied migrations. Think of a file in source control. The source control system may generate “version” numbers for reference, but it doesn’t use them to decide what should be merged in. When you say “merge the “1_1” branch into trunk, it simply takes all the changes in the “1_1” branch and applies the same changes to trunk, regardless of whether the file in trunk has a higher “version number” or not. The same logic needs to be applied to database updates.

There are attempts to solve the situation in the Ruby community. The work-around solution works by naming each change by a timestamp and storing all the executed changes in the schema_migrations table. While it is a step in the right direction, if you need to re-order the execution of changes after a commit by multiple developers or after a merge it can be difficult. Hopefully a good solution to the problem is created soon, or we will have to develop a port of Liquibase for Ruby :)

Viewing all 200 articles
Browse latest View live