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 procedure;
135pub mod resolution;
136pub mod shadow;
137pub mod sql;
138
139// Re-exports
140pub use diff::{
141 EnumAlterDiff, EnumDiff, FieldAlterDiff, FieldDiff, IndexDiff, ModelAlterDiff, ModelDiff,
142 SchemaDiff, SchemaDiffer, UniqueConstraint,
143};
144pub use engine::{
145 MigrationConfig, MigrationEngine, MigrationPlan, MigrationResult, MigrationStatus,
146};
147pub use error::{MigrateResult, MigrationError};
148pub use file::{MigrationFile, MigrationFileManager};
149pub use history::{MigrationHistoryRepository, MigrationLock, MigrationRecord};
150pub use introspect::{
151 ColumnInfo, ConstraintInfo, EnumInfo, IndexInfo, IntrospectionConfig, IntrospectionResult,
152 Introspector, SchemaBuilder, SkippedTable, TableInfo,
153};
154pub use resolution::{
155 ConflictStrategy, Resolution, ResolutionAction, ResolutionBuilder, ResolutionConfig,
156 ResolutionCounts, ResolutionWarning,
157};
158pub use shadow::{
159 FieldDrift, IndexDrift, SchemaDrift, ShadowConfig, ShadowDatabase, ShadowDatabaseManager,
160 ShadowDiffResult, ShadowState, detect_drift,
161};
162pub use procedure::{
163 // Procedure types
164 ChangeType, ParameterMode, ParallelSafety, ProcedureAlterDiff, ProcedureChange,
165 ProcedureDefinition, ProcedureDiff, ProcedureDiffer, ProcedureHistoryEntry, ProcedureLanguage,
166 ProcedureParameter, ProcedureSqlGenerator, ProcedureStore, ReturnColumn, Volatility,
167 // Trigger types
168 TriggerAlterDiff, TriggerDefinition, TriggerEvent, TriggerLevel, TriggerTiming,
169 // Event Scheduler types (MySQL)
170 EventAlterDiff, EventDiff, EventInterval, EventSchedule, IntervalUnit, OnCompletion,
171 ScheduledEvent,
172 // SQL Agent types (MSSQL)
173 JobSchedule, JobStep, NotifyLevel, ScheduleFrequency, SqlAgentJob, StepAction, StepType,
174 Weekday,
175 // MongoDB Atlas Triggers
176 AtlasOperation, AtlasTrigger, AtlasTriggerType, AuthOperation,
177};
178pub use sql::{MigrationSql, PostgresSqlGenerator};