package migra
Install
dune-project
Dependency
Authors
Maintainers
Sources
sha256=08ceb8f21f00227f21209484ef1eb8cc326ed26a280747798ec5b0b7c5fdd678
sha512=70050833623ba7801a0a31b1700f5473903335c8db2cdf4de5bbd1876180ec0a93907444b42f7a39b6af454303e0ee5f4c3543ca51378d6324583a26b93f535c
doc/README.html
Migra
A database migration tool and library for OCaml, supporting PostgreSQL, MariaDB/MySQL, and SQLite. Write plain-SQL migrations with up/down sections; run them from the CLI during development, or embed the library in your app and migrate on startup.
migra generate create_users # scaffold a timestamped migration
$EDITOR migrations/*_create_users.sql
migra migrate # apply pending migrations
migra status # see what's appliedFeatures
- Three databases from one tool - PostgreSQL, MariaDB/MySQL, SQLite - chosen automatically from the connection URL.
- Plain SQL, with
up/downsections in a single file. Full SQL is supported: dollar-quoted function bodies,DELIMITERfor MySQL routines, and comments. - Transaction-safe - each migration runs in a transaction and rolls back on failure (with a caveat for MySQL/MariaDB DDL - see Safety).
- Checksums & drift detection - editing an already-applied migration, a missing migration file, or an out-of-order migration are all caught before anything runs.
- Flexible rollback - by step, to a version, or all; plus
redo. --dry-runto preview,--database-urlto override the environment,--tableto customise the tracking table.- CLI and library - the CLI is a thin layer over the public
Migra.MigratorAPI.
Installation
opam install migra
# plus the driver(s) you need (optional dependencies):
opam install caqti-driver-postgresql # postgresql://, postgres://
opam install caqti-driver-mariadb # mariadb://, mysql://
opam install caqti-driver-sqlite3 # sqlite3://Supported databases
Migra detects the database from the URL scheme:
Database | URL examples |
|---|---|
PostgreSQL |
|
MariaDB / MySQL |
|
SQLite |
|
The URL comes from --database-url or the DATABASE_URL environment variable. Percent-encode special characters in credentials (@ -> %40).
Migration files
Files are named YYYYMMDDHHMMSS_description.sql and contain two sections:
-- +migrate up
CREATE TABLE users (id SERIAL PRIMARY KEY, email TEXT NOT NULL);
-- +migrate down
DROP TABLE users;For MySQL/MariaDB stored routines whose body contains semicolons, use a DELIMITER directive (as you would in the mysql client):
-- +migrate up
DELIMITER //
CREATE PROCEDURE addrow(IN n INT) BEGIN INSERT INTO t VALUES (n); END //
DELIMITER ;CLI
export DATABASE_URL="postgresql://localhost:5432/myapp"
migra generate <name> # create a new migration file
migra migrate # apply all pending migrations
migra status # show applied/pending migrations
migra rollback # roll back the most recent migration
migra redo # roll back the last migration and re-apply it
migra init # create the database
migra setup # create the database and migrate
migra drop # drop the database
migra reset # drop, recreate, and migrateCommon options: -d/--dir DIR (migrations directory, default migrations), -t/--table NAME (tracking table, default schema_migrations), -D/--database-url URL, -v/--verbose, and --dry-run (on migrate / rollback). rollback also takes --step N, --to VERSION, --all.
Library usage
Use the library to run migrations programmatically - for example, on web-app startup. See examples/migrate_on_startup.ml.
let migrate () =
let config = Migra.Migrator.make ~database_url:(Sys.getenv "DATABASE_URL") () in
match Lwt_main.run (Migra.Migrator.run config) with
| Error e ->
Printf.eprintf "migration error: %s\n" (Migra.Types.show_error e); exit 1
| Ok r when not (Migra.Migrator.succeeded r) ->
Printf.eprintf "%d migration(s) failed\n" r.failure_count; exit 1
| Ok r -> Printf.printf "applied %d migration(s)\n" r.success_count
(* e.g. before Dream.run: *)
let () = migrate (); Dream.run @@ Dream.logger @@ routerThe public API is two modules:
Migra.Migrator-run,rollback,redo,status,generate,pending_plan/rollback_plan; amakeconfig constructor; and an optional?on_eventcallback for progress reporting.Migra.Database- database lifecycle (create_database,drop_database) and URL helpers.
run/rollback return Error only when migrations could not be run at all (bad URL, connection failure, drift); a migration whose SQL fails surfaces as Ok with failure_count > 0 - check Migra.Migrator.succeeded.
Safety & validation
- Checksums. When a migration is applied, a checksum of its file is stored.
migratere-validates applied migrations first and refuses to run if a file was modified after being applied, has gone missing, or a new migration is older than the latest applied one. - Transactions. On PostgreSQL and SQLite, migrations (including DDL) are fully transactional. On MySQL/MariaDB, DDL statements implicitly commit and cannot be rolled back - this is a server limitation, so keep MySQL/MariaDB migrations to a single DDL change each.
Development
dune build
dune fmt # format (ocamlformat)
dune runtest # unit tests (no database needed)
# Integration tests need running databases; docker-compose.yml brings them up:
docker compose up -d --wait
DATABASE_URL=postgresql://postgres@localhost:5433/postgres \
MARIADB_URL=mariadb://root:root@127.0.0.1:3307/mysql \
dune exec test/test_integration.exe
docker compose downSee docs/ for troubleshooting and running the tests.
License
MIT - see LICENSE.