Flyway with MariaDB for Those of a Nervous Disposition
This article will get you up and running quickly with Flyway migrations on MariaDB or MySQL databases, from PowerShell.
I provide a GitHub project with some ready-made SQL migrations scripts for MariaDB databases. When you run them, from they will that will create a beefed-up copy of the classic Pubs
sample database in MariaDB. I demonstrate how to connect from Flyway to MariaDB, in PowerShell, while handling credentials securely, and then how to run “Flyway migrate” and report on the results.
Setting up MariaDB and Flyway
If you haven’t done so already, you will need to install MariaDB server. This is easiest done using the installer they provide on your workstation on ‘localhost’ (equivalent to the special IP address 127.0.0.1). I prefer to have an installation on a server that can be accessed over a network. You can have several instances on a single server as long as they are on different ports. The MariaDB installer does all the hard work for you.
You’ll also need to install Flyway. I use Chocolatey in PowerShell to do this job, because it means I can update all the software that I’ve installed in Chocolatey in one operation without having to attend to the process.
Having installed Chocolatey, you can then use it to install Flyway using the version of PowerShell with administrator privileges.
1 |
Choco install flyway.commandline /y |
Getting the Pubs database onto MariaDB
The PubsMariaDB folder of my PubsAndFlyway project, on GitHub, contains a build script for the beefed-up version of Pubs with extra data and, in the Scripts subfolder you’ll find the step-by-step Flyway migration scripts that build the original database (create all the tables), fill it with the original data and then do some fine-tuning of the table structures, so that we can then import a bigger volume of data. To get a local copy of this project, you might like to use my Get-FilesFromRepo
PowerShell cmdlet, which you can get from here via cut-n-paste.
If you’re new to PowerShell, I’d advise you to use the PowerShell ISE, but if you are using any version of PowerShell after v6, you’ll need to use ISE mode in Microsoft’s Visual Studio Code (VS Code). Just execute the Get-FilesFromRepo
code in the ISE, to load the cmdlet, and then run the following code to copy the PubsMariaDB project. I’ve put the project in a temporary directory and saved the location in $ProjectFolder
variable so we can use it to tell Flyway where to find it. Just set the path to the project folder where you store your Flyway projects
1 2 3 4 5 6 7 8 |
$ProjectFolder="$env:Temp\PubsAndFlyway\PubsMariaDB"; $Params = @{ 'Owner' = 'Phil-Factor'; 'Repository' = 'PubsAndFlyway'; 'RepoPath' = 'PubsMariaDB'; 'DestinationPath' = $ProjectFolder } Get-FilesFromRepo @Params |
We can check to make sure it all worked well:
1 2 3 4 |
If(!(Test-Path -path "$ProjectFolder\Scripts")) { Throw "could not find the script directory at $ProjectFolder\Scripts"} $Files = Get-Item -Path "$ProjectFolder\Scripts\*.SQL" If ($Files.Count -lt 5) { Throw "Files missing at $ProjectFolder\Scripts"} |
…and hopefully you’ll then see the project directory and the Scripts subdirectory.
Get started on MariaDB with Flyway in PowerShell
To do any serious work, you’ll need a good IDE to execute SQL code and to test things out. I use HeidiSQL for this, which is in the MariaDB distribution, or you can download from Chocolatey. HeidiSQL has the advantage of working with several databases (MariaDB, MySQL, MS SQL, PostgreSQL and SQLite). In HeidiSQL’s session manager, you click on the “New” button to create a new connection, and most default settings are already set for you, except for the password, which is the one you were asked to provide when you installed MariaDB server.
MariaDB, like SQLite, doesn’t understand schemas the same way as SQL Server or almost any other relational database system (every RDBMS that supports schemas has subtle differences in the way they do it). In MySQL, a schema is synonymous with a database, at the physical level, and the keywords SCHEMA
and DATABASE
are also synonymous.
Therefore, since we use different schemas in our pubs
database (mainly dbo
, but later versions of our extended Pubs
database use other schemas such as people
) we need to create these dbo
and people
schemas ‘schemas’ on the same server. I used HeidiSQL to do this.
Setting the connection details and getting the login credentials
Firstly, set an alias for Flyway to make it easier to use. You’ll need to change that if you have a different version, or you’ve installed it in a different place.
1 |
Set-Alias Flyway 'C:\ProgramData\chocolatey\lib\flyway.commandline\tools\flyway-7.8.1\flyway.cmd' -Scope local |
Now we’ll need to set up an array with credentials and maybe passwords. This will tell Flyway, and anything else we need to use, how to access the database. We are storing any passwords encrypted in the user area. We store passwords separately for every sever, database and RDBMS, so that if the same server hosts MariaDB and Postgres, they have separate credentials.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
$Server = 'MyServer' #the name or instance of MariaDB Server # more than one instance can be set up on a server or your # workstation if they are on different ports. E.g. # localhost:3306 & localhost:3307 # The host can be and IPv4 or IPv6 host name string, and in the # latter case it must be put inside square brackets, for example # “[1000:2000::abcd].” When host is not specified, the default # value of localhost is used. # see https://mariadb.com/kb/en/about-mariadb-connector-j/ $Database = 'dbo'; #The name of the schema/database that you are using $UserID = 'root' # your userid $port = '3306 # the port that the service is on $RDBMS = 'MariaDB' # needed in case you need to keep separate credentials for different RDBMS on the same server # We get all the arguments as a string array. $MyArgs = if (!([string]::IsNullOrEmpty($UserID))) #then it is using Windows Credentials { # we see if we've got these stored already $SqlEncryptedPasswordFile = "$env:USERPROFILE\$($UserID)-$($Server)-$($RDBMS).xml" # test to see if we know about the password in a secure string stored in the user area if (Test-Path -path $SqlEncryptedPasswordFile -PathType leaf) { #has already got this set for this login so fetch it $SqlCredentials = Import-CliXml $SqlEncryptedPasswordFile } else #then we have to ask the user for it (once only) { # hasn't got this set for this login $aborted = $false #in case the user doesn't want to enter the password $SqlCredentials = get-credential -Credential $UserID # Save in the user area if ($SqlCredentials -ne $null) #in case the user aborted { $SqlCredentials | Export-CliXml -Path $SqlEncryptedPasswordFile <# Export-Clixml only exports encrypted credentials on Windows. otherwise it just offers some obfuscation but does not provide encryption. #> } else { $aborted = $True } } if (!($Aborted)) { @("-url=jdbc:mariadb://$($Server):$port/$Database", "-locations=filesystem:$ProjectFolder\Scripts", <# the migration folder #> "-schemas=dbo,people", "-user=$($SqlCredentials.UserName)", "-password=$($SqlCredentials.GetNetworkCredential().password)") } else { $null } } else { @("-url=jdbc:mariadb://$($Server):$port;databaseName=$Database;integratedSecurity=true". "-locations=filesystem:$ProjectFolder\Scripts", "-schemas=dbo,people") <# the schemas/databases the database has #> } |
Doing a migration
Having run the above code to set everything up, we can, in the same PowerShell session, do several different operations:
1 2 3 4 5 6 7 8 9 |
Flyway -? flyway @MyArgs info # Prints the information about applied, current and pending migrations Flyway @MyArgs migrate # Migrates the database to the current version Flyway @MyArgs migrate -target="1.1.6" # Migrates the database to a particular version Flyway @MyArgs clean # Drops all objects in the configured schemas Flyway @MyArgs validate # Validates the applied migrations against the ones on the classpath Flyway @MyArgs undo # (teams version only) Undoes the most recently applied versioned migration Flyway @MyArgs baseline # Baselines an existing database at the baselineVersion flyway @MyArgs repair # Repairs the schema history table |
To build the MariaDB version of the Pubs database, you merely run:
1 |
Flyway @MyArgs migrate # Migrates the database to the current version |
Or if you want just the classic version …
1 2 |
Flyway @MyArgs clean # if at higher level, drop all objects in the configured schemas Flyway @MyArgs migrate -target="1.1.2" # Migrates the database to the classic version |
We can test it out by running
1 |
flyway @MyArgs info # Prints the information about applied, current and pending migrations |
…which should give…
Flyway Community Edition 7.8.1 by Redgate Database: jdbc:sqlite:C:\Users\MyName\sqlite\Pubs.sqlite3 (SQLite 3.34) Schema version: 1.1.5 +-----------+---------+-----------------------------+------+---------------------+---------+ | Category | Version | Description | Type | Installed On | State | +-----------+---------+-----------------------------+------+---------------------+---------+ | Versioned | 1.1.1 | Initial Build | SQL | 2021-07-27 16:33:52 | Success | | Versioned | 1.1.2 | Pubs Original Data | SQL | 2021-07-27 16:33:52 | Success | | Versioned | 1.1.3 | UseNVarcharetc | SQL | 2021-07-27 16:33:52 | Success | | Versioned | 1.1.4 | RenameConstraintsAdd tables | SQL | 2021-07-27 16:33:52 | Success | | Versioned | 1.1.5 | Add New Data | SQL | 2021-07-27 16:34:05 | Success | +-----------+---------+-----------------------------+------+---------------------+---------+
If you wish to be able to use this information, such as when you need to find out the current version of the database, you can read in a JSON version of the output:
1 2 3 |
$Result=flyway info @MyArgs -outputType=json |ConvertFrom-JSON # reads the information about applied, current and pending migrations $result.schemaVersion #The current version |
And we can get neater reporting, as follows:
1 |
$result.migrations|select version,description, state, InstalledBy| Format-Table |
version description state installedBy ------- ----------- ----- ----------- << Flyway Schema Creation >> Success Phil Factor 1.1.1 Initial Build Success Phil Factor 1.1.2 Pubs Original Data Success Phil Factor 1.1.3 UseNVarcharetc Success Phil Factor 1.1.4 RenameConstraintsAdd tables Success Phil Factor 1.1.5 Add New Data Success Phil Factor 1.1.6 Add Tags Success Phil Factor
Any time you need to rebuild the database, you can do this…
1 2 3 |
Flyway @MyArgs clean # Drops all objects in the configured schemas flyway @MyArgs info # Prints the information about applied, current and pending migrations flyway @MyArgs migrate # Migrates the database to the latest version |
Here is the database in its full splendor, as shown in HeidiSQL:
What can go wrong?
The magic of Flyway is that it can detect any errors in the migration and roll back the migration containing the error, so you can correct the error and attempt the migration again. It does this by executing your code within a transaction. With most database systems, some errors are considered too minor to automatically roll back a transaction, but Flyway makes sure every possible migration is either ‘all done’ or ‘not done at all’. Once an error happens, the whole sequence is stopped.
In most database systems, there are some DDL operations that can’t be reversed if an error occurs elsewhere in the migration. If Flyway detects that a migration cannot be run in a transaction, it will warn you and you must explicitly add a configuration file that allows Flyway to go ahead and use the file, acknowledging that you will have to manage the rollback.
Unfortunately, with MySQL and MariaDB, most ‘build’ SQL Code (DDL statements) cannot be rolled back when executed in a transaction because they are committed at the time they are executed. If a migration fails, you must ensure that the database is exactly as it was before the migration was attempted, and then you must run the Flyway Repair
action. Flyway can’t do this for you.
With reasonably small database development projects, the easiest course of action is to, instead, run the Clean
action to delete the database and then re-run the migrations to get to the previous successful migration. If you are updating production servers on the other hand, you risk costly data loss and, if the database is left in an indeterminate state, the probability of unpredictable points of failure.
Getting started with MariaDB is easy, but any development work needs to pick a good strategy for dealing with failed migrations that is both safe and convenient. I deal with some of these strategies in my next article, Dealing with Failed SQL Migrations in MariaDB or MySQL.
Conclusions
To introduce the idea of using Flyway with MariaDB, I’ve provided you with a partially made database for providing a system for book wholesalers. You can do no damage by playing around with migrations to improve it and expand it. MariaDB is a real multi-user relational database with many years of robust usage. It requires a rather different approach to development because of the way that it has implemented transactions, but I’ll cover that in more detail in a subsequent article.