Migrations¶
File structure¶
migrations/
├── versioned/
│ ├── 20260101000000_init.sql
│ └── 20260315093000_add_users.sql
└── repeatable/
├── users_v.sql
└── audit_fn.sql
Versioned migrations¶
Versioned migrations are applied once and tracked by checksum.
Format¶
-- ezmig:apply
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email TEXT NOT NULL UNIQUE
);
-- ezmig:rollback
DROP TABLE users;
-- ezmig:applymarks the section applied byezmig apply-- ezmig:rollbackmarks the section run byezmig rollback- The two sections can appear in any order — EZMig detects boundaries by marker position, not file order
- The rollback block is required if you want rollback support
Naming convention¶
EZMig sorts versioned migrations lexicographically by filename. The strongly recommended prefix is a 14-digit timestamp:
YYYYMMDDHHmmss_description.sql
Example:
20260323093000_add_payments_table.sql
20260323094512_add_audit_log.sql
Why timestamps over sequential numbers?
Sequential numbers (001, 002) break when two developers independently create
003_foo.sql and 003_bar.sql on separate branches. Timestamps are globally
unique per second per developer with no coordination needed.
Immutability¶
Once applied, a versioned migration is immutable:
- Its checksum is recorded in
ezmig_migrations - Editing the file causes a checksum drift error on the next
ezmig validate - To fix something, add a new migration with a later timestamp
Repeatable migrations¶
Repeatable migrations are re-applied whenever their content changes.
Format¶
CREATE OR REPLACE VIEW active_users AS
SELECT * FROM users WHERE active = true;
No -- ezmig:apply marker is needed. The entire file is executed.
Behaviour¶
- Identified by filename (used as primary key in
ezmig_migrations) - Tracked by checksum — re-applied when content changes
- Always applied after all versioned migrations in a given run
- Sorted alphabetically within themselves
Repeatable migrations are ideal for objects that can be safely recreated: views, functions, stored procedures, and packages.
Replay / force apply (dev)¶
For iterative development, you can force re-run migrations:
ezmig replay --migration "202601*.sql" --allow-replay --database dev
ezmig replay --repeatable "*.sql" --allow-replay --database dev
ezmig replay --all --allow-replay --database dev
# equivalent shortcut for replaying everything
ezmig apply --force --allow-replay --database dev
Behaviour:
- Applied versioned migrations: execute
-- ezmig:rollback, then-- ezmig:apply - Pending versioned migrations: apply normally
- Selected repeatables: execute even when checksum is unchanged
Replay/force is disabled by default; enable with --allow-replay or allow_replay = true on the target config.
Migration states¶
| State | Meaning |
|---|---|
PENDING |
Not yet applied |
APPLIED |
Applied and unchanged |
CHANGED |
Repeatable with a new checksum (will be re-applied) |
UNCHANGED |
Repeatable with the same checksum (skipped) |
UNKNOWN |
No adapter / offline mode |
Tracking table¶
EZMig records all applied migrations in:
CREATE TABLE ezmig_migrations (
version VARCHAR PRIMARY KEY,
type VARCHAR NOT NULL,
checksum VARCHAR NOT NULL,
applied_at TIMESTAMP NOT NULL,
execution_ms INTEGER
);