Versioned database migrations for Elixir libraries using Ecto.
EctoEvolver provides infrastructure for library authors to ship versioned database schemas that support incremental upgrades. Inspired by how Oban handles migrations.
EctoEvolver uses raw SQL instead of Ecto's schema DSL. This enables:
- Advanced database features - Functions, triggers, views, materialized views, RLS policies, and complex DDL that Ecto's DSL doesn't support
- Portability - SQL files work outside Elixir/Ecto, making migrations usable with any database client
- Transparency - Standard
.sqlfiles are readable and auditable without Elixir knowledge
EctoEvolver uses an adapter system for database-specific operations. The adapter is auto-detected from your Ecto repo's configuration at runtime.
Currently supported:
EctoEvolver.Adapters.Postgres— PostgreSQL (auto-detected, default)
Future adapters: The adapter architecture is designed to support other databases that Ecto supports (SQLite, MySQL, etc.). Contributions welcome.
def deps do
[
{:ecto_evolver, "~> 0.1.0"}
]
enddefmodule MyLibrary.Migration do
use EctoEvolver,
otp_app: :my_library,
default_prefix: "my_library",
versions: [MyLibrary.Migrations.V01],
tracking_object: {:table, "my_main_table"}
enddefmodule MyLibrary.Migrations.V01 do
use EctoEvolver.Version,
otp_app: :my_library,
version: "01",
sql_path: "my_library/sql/versions"
endPlace SQL files in your priv/ directory:
priv/my_library/sql/versions/
└── v01/
├── v01_up.sql
└── v01_down.sql
Use $SCHEMA$ as a placeholder for the schema name and --SPLIT-- to separate statements:
CREATE SCHEMA IF NOT EXISTS $SCHEMA$;
--SPLIT--
CREATE TABLE $SCHEMA$.my_table (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL
);defmodule MyApp.Repo.Migrations.AddMyLibrary do
use Ecto.Migration
def up, do: MyLibrary.Migration.up()
def down, do: MyLibrary.Migration.down()
end:otp_app- OTP application containing SQL files inpriv/:default_prefix- Default schema name:versions- List of version modules in order[V01, V02, ...]:tracking_object-{:view | :table | :materialized_view, "name"}for version tracking:adapter- Adapter module (optional, auto-detected from repo)
:otp_app- OTP application containing SQL files:version- Version string like"01","02":sql_path- Path withinpriv/to SQL versions directory
Version tracking is adapter-specific. The PostgreSQL adapter uses object comments:
COMMENT ON TABLE schema.my_table IS 'MyLibrary version=1';This allows EctoEvolver to detect the current version and apply only necessary migrations during upgrades.
MIT
