Changelog
View SourceAll notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[0.2.2] - 2026-05-28
Fixed
exec_sql(PG): replace broken SAVEPOINT approach withBEGIN/COMMIT/ROLLBACKset_version(all drivers): replace non-atomic DELETE+INSERT with upsert (PG:ON CONFLICT DO UPDATE, MySQL:REPLACE INTO, SQLite:INSERT OR REPLACE)apply_up/apply_down/version/force: replaceok = Driver:...badmatch patterns withcaseexpressions for proper{error, _}propagation- Table name injection: add
validate_table_name/1whitelist (^[a-zA-Z_][a-zA-Z0-9_]*$) in all three drivers driver/1: validate driver module is loaded viacode:which/1before use- Remove dead code:
clear_dirty/2export and function body removed fromerlang_migrate_pg - Fix
current_version/2edoc: correct return type to{ok, Version, Dirty} - README: fix PostgreSQL minimum version (
18+→10+), sync install example versions to0.2.2, fix epgsql version to4.8.0, correct Migration history table description
Refactored
- Extract
run_one_up/6andrun_one_down/7to reduceapply_up/apply_downnesting from 5 to 1 level
[0.2.1] - 2026-05-28
Fixed
- Fix edoc module header: use backtick+single-quote syntax (dir') instead of double backtick to resolve doc chunk generation error
[0.2.0] - 2026-05-28
Added
- Multi-database support via
driverconfig key (defaults toerlang_migrate_pg) erlang_migrate_mysql— MySQL 8+ driver usingmysql-otp,GET_LOCKadvisory lockerlang_migrate_sqlite— SQLite 3+ driver usingesqlite, OTPglobal:set_lockerlang_migrate_driverbehaviour — implement all 8 callbacks to add a new databasedown/1— roll back all applied migrationsgoto/2— jump to any version (auto up or down)version/1now returns{ok, Version, Dirty}(was{ok, Version})lock_timeoutconfig key — advisory lock wait timeout in ms (default 15 000)loggerconfig key — optionalfun(Level, Msg) -> okcallback- 33 EUnit tests for core logic + 24 tests for MySQL/SQLite drivers (57 total)
Changed
- Single-row tracking table — aligned with golang-migrate:
set_versionis nowDELETE + INSERTso the table always holds at most one row;force/2is correct with no stale dirty rows - Zero hard dependencies —
epgsql,mysql,esqliteare all opt-in; add only the driver you use to your ownrebar.config tableconfig key — customise the tracking table name without touchingerlang_migratesource
Fixed
force/2stale-dirty-row bug (multi-row model left dirty rows thatforcecould not clear; single-row model eliminates the issue)erlang:phash2range — was16#7FFFFFFFFFFFFFFF(too large); now1 bsl 30
[0.1.0] - 2026-05-28
Added
- Initial release
- Plain SQL migration files (
.up.sql/.down.sqlpairs) - Sequential integer versioning (
000001_title.up.sql) - Migration state stored in
schema_migrationstable in the target PostgreSQL database - PostgreSQL advisory lock — safe for concurrent multi-node Erlang clusters
- Dirty state machine — blocks further runs after partial failure
force/2to recover from dirty stateup/1,up/2— apply all or N pending migrationsdown/2— roll back N applied migrationsversion/1— query current versiondrop/1— drop theschema_migrationstable- Minimal dependencies: only
epgsql - 7 EUnit tests for file scanner (
erlang_migrate_source)