Crate prax_migrate

Crate prax_migrate 

Source
Expand description

§prax-migrate

Migration engine for the Prax ORM.

This crate provides functionality for:

  • Schema diffing between Prax schema definitions and database state
  • SQL migration generation for PostgreSQL (with MySQL/SQLite planned)
  • Migration file management on the filesystem
  • Migration history tracking in the database
  • Safe, transactional migration application and rollback
  • Resolution system for handling migration conflicts and checksums

§Architecture

The migration engine compares your Prax schema definition with the current database state and generates SQL scripts to bring the database up to date. It tracks applied migrations in a _prax_migrations table.

┌──────────────┐     ┌────────────────┐     ┌─────────────┐
│ Prax Schema  │────▶│ Schema Differ  │────▶│ SQL Gen     │
└──────────────┘     └────────────────┘     └─────────────┘
                             │                     │
                             ▼                     ▼
                     ┌────────────────┐     ┌─────────────┐
                     │ Migration Plan │────▶│ Apply SQL   │
                     └────────────────┘     └─────────────┘
                                                   │
                                                   ▼
                                           ┌─────────────┐
                                           │ History Tbl │
                                           └─────────────┘

§Example

use prax_migrate::{MigrationConfig, MigrationEngine};

async fn run_migrations() -> Result<(), Box<dyn std::error::Error>> {
    // Parse your schema
    let schema = prax_schema::parse_schema(r#"
        model User {
            id      Int @id @auto
            email   String @unique
            name    String?
        }
    "#)?;

    // Configure migrations
    let config = MigrationConfig::new()
        .migrations_dir("./migrations");

    // Create engine with your history repository
    let history = /* your history implementation */;
    let engine = MigrationEngine::new(config, history);

    // Initialize (creates migrations table)
    engine.initialize().await?;

    // Plan migrations
    let plan = engine.plan(&schema).await?;
    println!("Plan: {}", plan.summary());

    // Apply migrations
    let result = engine.migrate().await?;
    println!("Applied {} migrations in {}ms",
        result.applied_count, result.duration_ms);

    Ok(())
}

§Migration Files

Migrations are stored as directories with up.sql and down.sql files:

migrations/
├── 20231215120000_create_users/
│   ├── up.sql
│   └── down.sql
├── 20231216090000_add_posts/
│   ├── up.sql
│   └── down.sql
└── resolutions.toml        # Migration resolutions

§Resolution System

The resolution system handles common migration issues:

  • Checksum Mismatches: When a migration is modified after being applied
  • Skipped Migrations: Intentionally skip migrations (e.g., legacy tables)
  • Baseline Migrations: Mark migrations as applied without running them
  • Renamed Migrations: Map old migration IDs to new ones
  • Conflict Resolution: Handle conflicts between migrations
use prax_migrate::{Resolution, ResolutionConfig};

let mut resolutions = ResolutionConfig::new();

// Accept a checksum change
resolutions.add(Resolution::accept_checksum(
    "20240101_create_users",
    "old_checksum",
    "new_checksum",
    "Fixed column type",
));

// Skip a migration
resolutions.add(Resolution::skip(
    "20240102_legacy_table",
    "Already exists in production",
));

// Mark as baseline (applied without running)
resolutions.add(Resolution::baseline(
    "20240103_initial",
    "Database was imported from backup",
));

// Save to file
resolutions.save("migrations/resolutions.toml").await?;

Re-exports§

pub use diff::EnumAlterDiff;
pub use diff::EnumDiff;
pub use diff::FieldAlterDiff;
pub use diff::FieldDiff;
pub use diff::IndexDiff;
pub use diff::ModelAlterDiff;
pub use diff::ModelDiff;
pub use diff::SchemaDiff;
pub use diff::SchemaDiffer;
pub use diff::UniqueConstraint;
pub use engine::MigrationConfig;
pub use engine::MigrationEngine;
pub use engine::MigrationPlan;
pub use engine::MigrationResult;
pub use engine::MigrationStatus;
pub use error::MigrateResult;
pub use error::MigrationError;
pub use file::MigrationFile;
pub use file::MigrationFileManager;
pub use history::MigrationHistoryRepository;
pub use history::MigrationLock;
pub use history::MigrationRecord;
pub use introspect::ColumnInfo;
pub use introspect::ConstraintInfo;
pub use introspect::EnumInfo;
pub use introspect::IndexInfo;
pub use introspect::IntrospectionConfig;
pub use introspect::IntrospectionResult;
pub use introspect::Introspector;
pub use introspect::SchemaBuilder;
pub use introspect::SkippedTable;
pub use introspect::TableInfo;
pub use resolution::ConflictStrategy;
pub use resolution::Resolution;
pub use resolution::ResolutionAction;
pub use resolution::ResolutionBuilder;
pub use resolution::ResolutionConfig;
pub use resolution::ResolutionCounts;
pub use resolution::ResolutionWarning;
pub use shadow::FieldDrift;
pub use shadow::IndexDrift;
pub use shadow::SchemaDrift;
pub use shadow::ShadowConfig;
pub use shadow::ShadowDatabase;
pub use shadow::ShadowDatabaseManager;
pub use shadow::ShadowDiffResult;
pub use shadow::ShadowState;
pub use shadow::detect_drift;
pub use sql::MigrationSql;
pub use sql::PostgresSqlGenerator;

Modules§

diff
Schema diffing for generating migrations.
engine
Migration engine implementation.
error
Error types for the migration engine.
file
Migration file management.
history
Migration history tracking.
introspect
Database introspection for reverse-engineering schemas.
resolution
Migration resolution system.
shadow
Shadow database support for safe migration testing.
sql
SQL generation for migrations.