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};