Ormer
Home
Quick Start
GitHub
  • 简体中文
  • English
Home
Quick Start
GitHub
  • 简体中文
  • English
  • Introduction to Ormer
  • Quick Start
  • Model Definition
  • Database Connection
  • Data Operations
  • Query Builder
  • Advanced Queries
  • Transaction Management
  • Connection Pool
  • Hooks System
  • Migrations

Migrations

Ormer supports two migration styles: inspectable schema plans derived from a model, and versioned migrations implemented with the Migration trait.

Plan From A Model

migrate_table::<T>() builds an executable schema migration plan. A missing table produces a create-table step, and a missing non-primary-key column produces ADD COLUMN; existing-column type or nullability drift is converted into real migration steps where Ormer can do so safely. SQLite uses a table rebuild when needed. Conversions that cannot be inferred safely return an error.

let plan = db.migrate_table::<User>().plan().await?;

println!("{}", plan.to_sql()?);
for warning in plan.warnings() {
    eprintln!("{warning}");
}

db.migrate_table::<User>().execute().await?;

If a target conversion cannot be proven safe, plan() or execute() returns an error. SQLite complex changes are applied by rebuilding the table, and invalid legacy values fail the migration and roll back.

Versioned Migrations

Implement Migration and pass migrations to db.migrations():

use ormer::{Migration, MigrationRunner, MigrationStep};

struct AddUserEmail;

impl Migration for AddUserEmail {
    fn version(&self) -> u64 {
        1
    }

    fn name(&self) -> &str {
        "add_user_email"
    }

    fn up(&self) -> Vec<MigrationStep> {
        vec![MigrationStep::AddColumn {
            table: "users".into(),
            column: "email".into(),
            definition: "TEXT".into(),
        }]
    }

    fn down(&self) -> Vec<MigrationStep> {
        vec![MigrationStep::Sql {
            sql: "DROP COLUMN email".into(),
        }]
    }
}

let migrations = [AddUserEmail];
let runner: MigrationRunner<'_, AddUserEmail> = db.migrations(&migrations);

let pending = runner.pending().await?;
println!("pending: {}", pending.len());
let applied = runner.execute().await?;
println!("applied: {applied}");

When a runner is not needed, call the database methods directly:

let pending = db.pending_migrations(&migrations).await?;
let applied = db.apply_migrations(&migrations).await?;

Applied migrations are recorded in __ormer_migrations. Migrations are sorted by version, run in a transaction, and store a checksum derived from the name and up() steps; changing an applied migration returns an error.

Available MigrationStep variants are CreateType, AlterType, CreateTable, AddColumn, BackfillColumn, AlterColumn, AddConstraint, CreateIndex, AddForeignKey, and Sql. Use Sql for complex or dialect-specific DDL.

let history = db.migration_history().await?;
for migration in history {
    println!("{} {} {}", migration.version, migration.name, migration.checksum);
}

SQLite cannot add a foreign key after table creation; MigrationStep::AddForeignKey returns an error on SQLite.

Last Updated: 8/5/26, 10:14 PM
Contributors: fawdlstty
Prev
Hooks System