prax_migrate/lib.rs
1//! # prax-migrate
2//!
3//! Migration engine for the Prax ORM.
4//!
5//! This crate provides functionality for:
6//! - Schema diffing between Prax schema definitions and database state
7//! - SQL migration generation for PostgreSQL (with MySQL/SQLite planned)
8//! - Migration file management on the filesystem
9//! - Migration history tracking in the database
10//! - Safe, transactional migration application and rollback
11//! - **Resolution system** for handling migration conflicts and checksums
12//!
13//! ## Architecture
14//!
15//! The migration engine compares your Prax schema definition with the current
16//! database state and generates SQL scripts to bring the database up to date.
17//! It tracks applied migrations in a `_prax_migrations` table.
18//!
19//! ```text
20//! ┌──────────────┐ ┌────────────────┐ ┌─────────────┐
21//! │ Prax Schema │────▶│ Schema Differ │────▶│ SQL Gen │
22//! └──────────────┘ └────────────────┘ └─────────────┘
23//! │ │
24//! ▼ ▼
25//! ┌────────────────┐ ┌─────────────┐
26//! │ Migration Plan │────▶│ Apply SQL │
27//! └────────────────┘ └─────────────┘
28//! │
29//! ▼
30//! ┌─────────────┐
31//! │ History Tbl │
32//! └─────────────┘
33//! ```
34//!
35//! ## Example
36//!
37//! ```rust,ignore
38//! use prax_migrate::{MigrationConfig, MigrationEngine};
39//!
40//! async fn run_migrations() -> Result<(), Box<dyn std::error::Error>> {
41//! // Parse your schema
42//! let schema = prax_schema::parse_schema(r#"
43//! model User {
44//! id Int @id @auto
45//! email String @unique
46//! name String?
47//! }
48//! "#)?;
49//!
50//! // Configure migrations
51//! let config = MigrationConfig::new()
52//! .migrations_dir("./migrations");
53//!
54//! // Create engine with your history repository
55//! let history = /* your history implementation */;
56//! let engine = MigrationEngine::new(config, history);
57//!
58//! // Initialize (creates migrations table)
59//! engine.initialize().await?;
60//!
61//! // Plan migrations
62//! let plan = engine.plan(&schema).await?;
63//! println!("Plan: {}", plan.summary());
64//!
65//! // Apply migrations
66//! let result = engine.migrate().await?;
67//! println!("Applied {} migrations in {}ms",
68//! result.applied_count, result.duration_ms);
69//!
70//! Ok(())
71//! }
72//! ```
73//!
74//! ## Migration Files
75//!
76//! Migrations are stored as directories with `up.sql` and `down.sql` files:
77//!
78//! ```text
79//! migrations/
80//! ├── 20231215120000_create_users/
81//! │ ├── up.sql
82//! │ └── down.sql
83//! ├── 20231216090000_add_posts/
84//! │ ├── up.sql
85//! │ └── down.sql
86//! └── resolutions.toml # Migration resolutions
87//! ```
88//!
89//! ## Resolution System
90//!
91//! The resolution system handles common migration issues:
92//!
93//! - **Checksum Mismatches**: When a migration is modified after being applied
94//! - **Skipped Migrations**: Intentionally skip migrations (e.g., legacy tables)
95//! - **Baseline Migrations**: Mark migrations as applied without running them
96//! - **Renamed Migrations**: Map old migration IDs to new ones
97//! - **Conflict Resolution**: Handle conflicts between migrations
98//!
99//! ```rust,ignore
100//! use prax_migrate::{Resolution, ResolutionConfig};
101//!
102//! let mut resolutions = ResolutionConfig::new();
103//!
104//! // Accept a checksum change
105//! resolutions.add(Resolution::accept_checksum(
106//! "20240101_create_users",
107//! "old_checksum",
108//! "new_checksum",
109//! "Fixed column type",
110//! ));
111//!
112//! // Skip a migration
113//! resolutions.add(Resolution::skip(
114//! "20240102_legacy_table",
115//! "Already exists in production",
116//! ));
117//!
118//! // Mark as baseline (applied without running)
119//! resolutions.add(Resolution::baseline(
120//! "20240103_initial",
121//! "Database was imported from backup",
122//! ));
123//!
124//! // Save to file
125//! resolutions.save("migrations/resolutions.toml").await?;
126//! ```
127
128pub mod diff;
129pub mod engine;
130pub mod error;
131pub mod file;
132pub mod history;
133pub mod introspect;
134pub mod resolution;
135pub mod shadow;
136pub mod sql;
137
138// Re-exports
139pub use diff::{
140 EnumAlterDiff, EnumDiff, FieldAlterDiff, FieldDiff, IndexDiff, ModelAlterDiff, ModelDiff,
141 SchemaDiff, SchemaDiffer, UniqueConstraint,
142};
143pub use engine::{
144 MigrationConfig, MigrationEngine, MigrationPlan, MigrationResult, MigrationStatus,
145};
146pub use error::{MigrateResult, MigrationError};
147pub use file::{MigrationFile, MigrationFileManager};
148pub use history::{MigrationHistoryRepository, MigrationLock, MigrationRecord};
149pub use introspect::{
150 ColumnInfo, ConstraintInfo, EnumInfo, IndexInfo, IntrospectionConfig, IntrospectionResult,
151 Introspector, SchemaBuilder, SkippedTable, TableInfo,
152};
153pub use resolution::{
154 ConflictStrategy, Resolution, ResolutionAction, ResolutionBuilder, ResolutionConfig,
155 ResolutionCounts, ResolutionWarning,
156};
157pub use shadow::{
158 FieldDrift, IndexDrift, SchemaDrift, ShadowConfig, ShadowDatabase, ShadowDatabaseManager,
159 ShadowDiffResult, ShadowState, detect_drift,
160};
161pub use sql::{MigrationSql, PostgresSqlGenerator};