orm.migration #
journal_table is the only table the runner manages itself. It holds one row per applied migration; anything else in the database belongs to the migrations.
fn down #
fn down[T](mut db T, migrations []Migration[T], steps int) !int
down reverts the steps most recently applied listed migrations, newest first, forgetting each one from the journal. Applied versions missing from the list are left alone: the runner only reverts what it can see.
fn status #
fn status[T](mut db T, migrations []Migration[T]) ![]MigrationStatus
status pairs every listed migration, in list order, with whether the journal holds it.
fn up #
fn up[T](mut db T, migrations []Migration[T]) !int
up applies every listed migration the journal does not hold yet, oldest first, recording each one, and returns how many ran. An empty result means the database is current, not that anything failed.
struct Migration #
struct Migration[T] {
pub:
version string @[required]
up fn (mut db T) ! @[required]
down fn (mut db T) ! @[required]
}
Migration is one versioned schema change: up applies it, down reverts it, and version orders it. Versions sort as strings, so stamp them YYYYMMDDHHMMSS_name, e.g. 20241015120000_create_users; anything outside [A-Za-z0-9_] is rejected rather than interpolated into SQL.
A migration file conventionally lives at db/migrations/<version>.v and returns its struct from a migration() function over the app's concrete database type, so up bodies can use the full sql db {} DSL:
import db.sqlite
import orm.migration
pub fn migration() migration.Migration[sqlite.DB] {
return migration.Migration[sqlite.DB]{
version: '20241015120000_create_users'
up: fn (mut db sqlite.DB) ! {
sql db {
create table User
}!
}
down: fn (mut db sqlite.DB) ! {
sql db {
drop table User
}!
}
}
}
struct MigrationStatus #
struct MigrationStatus {
pub:
version string
applied bool
}
MigrationStatus pairs one listed migration with whether the journal holds it.