-
Notifications
You must be signed in to change notification settings - Fork 77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: Execute a directory tree of "code migration" scripts for each hook. #59
Comments
Hi, Thank you for sharing your method! Here is a JavaScript (Nashorn) example. var Arrays = Java.type("java.util.Arrays");
var File = Java.type("java.io.File");
var FileFilter = Java.type("java.io.FileFilter");
var FilenameFilter = Java.type("java.io.FilenameFilter");
var Files = Java.type("java.nio.file.Files");
var Paths = Java.type("java.nio.file.Paths");
var StandardCharsets = Java.type("java.nio.charset.StandardCharsets");
var MessageDigest = Java.type("java.security.MessageDigest");
var DatatypeConverter = Java.type("javax.xml.bind.DatatypeConverter");
// In production.properties, add ':ignoreChecksum=true' to the hook def
var ignoreChecksum = (typeof ignoreChecksum == undefined) ? false : java.lang.Boolean.valueOf('' + ignoreChecksum);
// Get an array of dirs in 'code' dir
var dirs = new File(migrationPaths.getBasePath(), "code").listFiles(new FileFilter() {
accept: function(item) {
return item.isDirectory();
}
});
// Sort by path
Arrays.sort(dirs);
// Get an array of scripts in the dir
Arrays.asList(dirs).forEach(function(dir) {
var scripts = dir.listFiles(new FilenameFilter() {
accept: function(dir, name) {
return name.endsWith(".sql");
}
});
// Sort by path
Arrays.sort(scripts);
Arrays.asList(scripts).forEach(function(script) {
// Read script file as a byte array
var bytes = Files.readAllBytes(script.toPath());
// Calculate checksum
var newChecksum = DatatypeConverter.printHexBinary(MessageDigest.getInstance("MD5").digest(bytes));
// Assuming checksum is saved in the same dir with an extension.
var checksumPath = script.getAbsolutePath() + ".md5";
// Read the saved checksum from the file.
var oldChecksum = readChecksum(checksumPath);
// Compare checksum
if (ignoreChecksum || oldChecksum != newChecksum) {
// New or updated.
print('Execute : ' + script);
var sql = new java.lang.String(bytes, StandardCharsets.UTF_8);
hookContext.executeSql(sql);
saveChecksum(checksumPath, newChecksum);
} else {
print('Not modified : ' + script);
}
});
});
function readChecksum(path) {
var p = Paths.get(path);
if (Files.exists(p)) {
return Files.readAllLines(p, StandardCharsets.UTF_8).get(0);
}
return "";
}
function saveChecksum(path, checksum) {
Files.write(Paths.get(path), checksum.getBytes(StandardCharsets.UTF_8), java.nio.file.StandardOpenOption.CREATE);
} # development.properties
hook_after_up=js:update_code.js # production.properties
hook_after_up=js:update_code.js:ignoreChecksum=true As a side note, I had the impression that it might be better to create a standalone Java app so that you can perform the task at any time (independently of migration).
As it is an advanced topic, how about a wiki page? Hope this helps, |
Thanks so much for this code sample. A long overdue thanks! |
Database migrations can be split into two types ...
Example for Code migrations see Flyway's Repeatable Scripts.
up
ordown
but you want the option to perform this to check that no code is broken by the change.To execute these code scripts, the simplest way is to just place them all in a directory and add a feature to execute a directory tree containing SQL scripts to drop/create each code object. To ensure consistent state, execution must be in sorted file name order. This is my feature request.
Code migrations must also be executed in dependency order (assuming we have one script per object). This is a partial ordering so we don't need to number every code script to do this. We simply create numbered sub-directories that correspond to their dependency level. So for example ...
code/views/01/myview1.sql
- Level 01 objects don't have any dependencies.code/views/01/myview2.sql
code/views/02/myview3.sql
- Level 02 objects depend on Level 01 objects.code/views/03/myview4.sql
- Level 03 objects depend on Level 01 & 02 objects.etc.
I've implemented this for another migration tool FluentMigrator.
So what am I asking for?
v1 Solution
This the simplest "available now" solution.
v2 Solution
Allow ALL hooks to look for a folder corresponding to the hook name under the scripts folder and execute all SQL in the folder's sub-directories in path name order .
up
anddown
and when generating scripts using thescript
command, provide an option to execute all the hooked code scripts.The text was updated successfully, but these errors were encountered: