Skip to content

Conversation

@pikaju
Copy link
Contributor

@pikaju pikaju commented Jan 8, 2026

Summary

Adds a complete migration system to Toasty: a CLI that diffs your Rust models against the last known schema snapshot, generates SQL migration files, and applies them to your database. Supports SQLite, PostgreSQL, and MySQL, with database-aware DDL generation (e.g. SQLite's table-recreation pattern for column type changes).

Usage

1. Configure your project with a Toasty.toml at the root:

[migration]
path = "toasty"
prefix_style = "Sequential"
checksums = false
statement_breakpoints = true

2. Wire up the CLI in a binary (src/bin/cli.rs):

use toasty_cli::{Config, ToastyCli};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let config = Config::load()?;
    let db = create_db().await?;
    let cli = ToastyCli::with_config(db, config);
    cli.parse_and_run().await
}

3. Generate a migration when your models change:

cargo run --bin cli -- migration generate --name initial

This diffs your current schema against the last snapshot and produces a SQL migration file. If it detects a potential rename (table, column, or index), it interactively prompts you:

  Table "users" is missing
  > Drop "users" ✖
    Rename "users" → "people"

4. Apply pending migrations to your database:

cargo run --bin cli -- migration apply

5. Drop a migration (interactive picker or by name):

cargo run --bin cli -- migration drop --latest
cargo run --bin cli -- migration drop --name 0001_migration.sql

6. Print the current schema snapshot:

cargo run --bin cli -- migration snapshot

What generated files look like

A generated migration (toasty/migrations/0000_migration.sql):

CREATE TABLE "users" (
    "id" TEXT NOT NULL,
    "name" TEXT NOT NULL,
    "email" TEXT NOT NULL,
    PRIMARY KEY ("id")
);
-- #[toasty::breakpoint]
CREATE UNIQUE INDEX "index_users_by_id" ON "users" ("id");
-- #[toasty::breakpoint]
CREATE UNIQUE INDEX "index_users_by_email" ON "users" ("email");
-- #[toasty::breakpoint]
CREATE TABLE "todos" (
    "id" TEXT NOT NULL,
    "user_id" TEXT NOT NULL,
    "title" TEXT NOT NULL,
    "completed" BOOLEAN NOT NULL,
    PRIMARY KEY ("id")
);
-- #[toasty::breakpoint]
CREATE INDEX "index_todos_by_user_id" ON "todos" ("user_id");

Each statement is separated by -- #[toasty::breakpoint] markers so the driver can execute them individually within a transaction.

The corresponding snapshot (toasty/snapshots/0000_snapshot.toml) captures the full schema state in TOML:

version = 1

[schema]

[[schema.tables]]
id = 0
name = "users"
primary_key = { columns = [{ table = 0, index = 0 }], ... }

[[schema.tables.columns]]
id = { table = 0, index = 0 }
name = "id"
ty = "String"
storage_ty = "Text"
nullable = false
auto_increment = false

[[schema.tables.columns]]
id = { table = 0, index = 1 }
name = "name"
# ...

A history.toml file ties migrations to their snapshots:

version = 1

[[migrations]]
id = 1101269131896407524
name = "0000_migration.sql"
snapshot_name = "0000_snapshot.toml"

Implementation details

  • Schema diffing (toasty-core): Rename-aware diff algorithms for tables, columns, and indices with a RenameHints system to distinguish renames from drop+create
  • SQL generation (toasty-sql): New DDL statement types (ALTER TABLE, ALTER COLUMN, ADD/DROP COLUMN, DROP INDEX, COPY TABLE, PRAGMA) with per-database serialization
  • Driver capabilities: New SchemaMutations capability struct -- SQLite can't ALTER COLUMN (uses table recreation), MySQL supports atomic multi-property ALTER COLUMN, PostgreSQL requires separate statements per property change
  • Driver trait: Extended with generate_migration(), applied_migrations(), and apply_migration(). Migrations tracked in a __toasty_migrations table, applied within transactions. DynamoDB stubbed as unimplemented.
  • ~1,880 lines of tests across 6 test files covering create/drop/alter table, add/drop/alter column for SQLite, PostgreSQL, and MySQL

Stats

~6,000 lines added across 70 files (new toasty-cli crate, extensions to toasty-core, toasty-sql, and all SQL drivers)

@pikaju pikaju changed the title Add database schema diffing for migrations Add database migration CLI tool Feb 8, 2026
@pikaju pikaju marked this pull request as ready for review February 8, 2026 17:01
@pikaju pikaju requested a review from carllerche February 8, 2026 20:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant