Skip to main content

reinhardt_db/
migrations.rs

1//! # Reinhardt Migrations
2//!
3//! Database migration system for Reinhardt framework.
4//!
5//! ## Features
6//!
7//! - **Auto-detection**: Detects model changes and generates migrations
8//! - **Migration Graph**: Manages dependencies between migrations
9//! - **AST-Based Entry Points**: Generates Rust 2024 Edition-compliant module files
10//! - **State Reconstruction**: Django-style `ProjectState` building from migration history
11//! - **Zero Downtime**: Support for safe schema changes in production
12//!
13//! ## AST-Based Entry Point Generation
14//!
15//! The `makemigrations` command uses Abstract Syntax Tree (AST) parsing to generate
16//! and maintain migration entry point files (`migrations/app_name.rs`). This ensures:
17//!
18//! 1. **Rust 2024 Edition Compliance**: Uses `app_name.rs` instead of deprecated `mod.rs`
19//! 2. **Robust Module Detection**: Structurally identifies existing migration modules
20//! 3. **Consistent Formatting**: Standardized output via `prettyplease`
21//!
22//! ### Generated Entry Point Example
23//!
24//! The migration system automatically generates entry point files:
25//!
26//! ```rust,ignore
27//! // migrations/myapp.rs (auto-generated - example only)
28//! pub mod _0001_initial;
29//! pub mod _0002_add_field;
30//!
31//! pub fn all_migrations() -> Vec<fn() -> Migration> {
32//!     vec![_0001_initial::migration, _0002_add_field::migration]
33//! }
34//! ```
35//!
36//! This file is automatically updated when new migrations are created.
37
38pub mod ast_parser;
39pub mod auto_migration;
40pub mod autodetector;
41pub mod dependency;
42pub mod di_support;
43pub mod executor;
44pub mod fields;
45pub mod graph;
46pub mod introspect;
47pub mod introspection;
48pub mod migration;
49pub mod migration_namer;
50pub mod migration_numbering;
51pub mod model_registry;
52pub mod operation_trait;
53pub mod operations;
54pub mod plan;
55pub mod recorder;
56pub mod registry;
57pub mod repository;
58pub mod schema_diff;
59pub mod schema_editor;
60pub mod service;
61pub mod source;
62pub mod squash;
63pub mod state_loader;
64pub mod visualization;
65pub mod zero_downtime;
66
67pub use crate::contenttypes::migration::MigrationRecord;
68pub use autodetector::{
69	// Pattern Learning and Inference
70	ChangeTracker,
71	ConstraintDefinition,
72	DetectedChanges,
73	FieldState,
74	ForeignKeyAction,
75	ForeignKeyConstraintInfo,
76	ForeignKeyInfo,
77	IndexDefinition,
78	InferenceEngine,
79	InferenceRule,
80	InferredIntent,
81	InteractiveAutodetector,
82	MigrationAutodetector,
83	MigrationPrompt,
84	ModelState,
85	OperationRef,
86	PatternMatcher,
87	ProjectState,
88	RuleCondition,
89	SimilarityConfig,
90	to_snake_case,
91};
92pub use dependency::{
93	DependencyCondition, DependencyResolutionContext, DependencyResolver, MigrationDependency,
94	OptionalDependency, SwappableDependency,
95};
96pub use di_support::{MigrationConfig, MigrationService as DIMigrationService};
97pub use executor::{DatabaseMigrationExecutor, ExecutionResult, OperationOptimizer};
98pub use fields::FieldType;
99pub use graph::{MigrationGraph, MigrationKey, MigrationNode};
100pub use migration::Migration;
101pub use migration_namer::MigrationNamer;
102pub use migration_numbering::MigrationNumbering;
103pub use model_registry::{
104	FieldMetadata, ManyToManyMetadata, ModelMetadata, ModelRegistry, RelationshipMetadata,
105	global_registry,
106};
107pub use operation_trait::MigrationOperation;
108pub use operations::{
109	AddColumn, AlterColumn, AlterTableOptions, BulkLoadFormat, BulkLoadOptions, BulkLoadSource,
110	ColumnDefinition, Constraint, CreateTable, DeferrableOption, DropColumn, IndexType,
111	InterleaveSpec, MySqlAlgorithm, MySqlLock, Operation, PartitionDef, PartitionOptions,
112	PartitionType, PartitionValues, SqlDialect, field_type_string_to_field_type,
113};
114pub use plan::{MigrationPlan, TransactionMode};
115
116// New operations from refactored modules
117pub use auto_migration::{
118	AutoMigrationError, AutoMigrationGenerator, AutoMigrationResult, ValidationResult,
119};
120pub use operations::{
121	AddField, AlterField, CreateCollation, CreateExtension, CreateModel, DeleteModel,
122	DropExtension, FieldDefinition, MoveModel, RemoveField, RenameField, RenameModel, RunCode,
123	RunSQL, StateOperation, special::DataMigration,
124};
125pub use recorder::{DatabaseMigrationRecorder, MigrationRecorder};
126pub use repository::{MigrationRepository, filesystem::FilesystemRepository};
127pub use schema_diff::{
128	ColumnSchema, ConstraintSchema, DatabaseSchema, ForeignKeySchemaInfo, IndexSchema, SchemaDiff,
129	SchemaDiffResult, TableSchema,
130};
131pub use schema_editor::SchemaEditor;
132pub use service::MigrationService;
133pub use source::{
134	MigrationSource, composite::CompositeSource, filesystem::FilesystemSource,
135	registry::RegistrySource,
136};
137pub use squash::{MigrationSquasher, SquashOptions};
138pub use state_loader::MigrationStateLoader;
139pub use visualization::{HistoryEntry, MigrationStats, MigrationVisualizer, OutputFormat};
140pub use zero_downtime::{MigrationPhase, Strategy, ZeroDowntimeMigration};
141
142pub use introspect::{
143	GeneratedFile, GeneratedOutput, GenerationConfig, IntrospectConfig, NamingConvention,
144	OutputConfig, SchemaCodeGenerator, TableFilterConfig, TypeMapper, TypeMappingError,
145	escape_rust_keyword, generate_models, preview_output, sanitize_identifier, to_pascal_case,
146	write_output,
147};
148pub use introspection::{
149	ColumnInfo, DatabaseIntrospector, ForeignKeyInfo as IntrospectionForeignKeyInfo, IndexInfo,
150	TableInfo, UniqueConstraintInfo,
151};
152
153// Re-export types from reinhardt-backends for convenience
154pub use crate::backends::{DatabaseConnection, DatabaseType};
155
156use thiserror::Error;
157
158/// Trait for types that provide migrations.
159///
160/// This trait enables compile-time migration collection, which is necessary
161/// because Rust cannot dynamically load code at runtime like Python's Django.
162///
163/// # Example
164///
165/// Application-side implementation (migration modules would be generated):
166///
167/// ```rust,ignore
168/// use reinhardt_db::migrations::{Migration, MigrationProvider};
169///
170/// // In your application's migrations module
171/// // These modules would be generated by `makemigrations` command:
172/// // pub mod _0001_initial;
173/// // pub mod _0002_add_published;
174///
175/// pub struct PollsMigrations;
176///
177/// impl MigrationProvider for PollsMigrations {
178///     fn migrations() -> Vec<Migration> {
179///         vec![
180///             _0001_initial::migration(),
181///             _0002_add_published::migration(),
182///         ]
183///     }
184/// }
185///
186/// // Usage in tests:
187/// // let (container, db) = postgres_with_migrations_from::<PollsMigrations>().await;
188/// ```
189pub trait MigrationProvider {
190	/// Returns all migrations provided by this type.
191	///
192	/// Migrations should be returned in dependency order (base migrations first).
193	fn migrations() -> Vec<Migration>;
194}
195
196#[derive(Debug, Error)]
197pub enum MigrationError {
198	#[error("Migration not found: {0}")]
199	NotFound(String),
200
201	#[error("Dependency error: {0}")]
202	DependencyError(String),
203
204	#[error("SQL error: {0}")]
205	SqlError(#[from] sqlx::Error),
206
207	#[error("Database error: {0}")]
208	DatabaseError(#[from] crate::backends::QueryDatabaseError),
209
210	#[error("Invalid migration: {0}")]
211	InvalidMigration(String),
212
213	#[error("Irreversible migration: {0}")]
214	IrreversibleError(String),
215
216	#[error("IO error: {0}")]
217	IoError(#[from] std::io::Error),
218
219	#[error("Format error: {0}")]
220	FmtError(#[from] std::fmt::Error),
221
222	#[error("Circular dependency detected: {cycle}")]
223	CircularDependency { cycle: String },
224
225	#[error("Node not found: {message} - {node}")]
226	NodeNotFound { message: String, node: String },
227
228	#[error("Introspection error: {0}")]
229	IntrospectionError(String),
230
231	#[error("Unsupported database: {0}")]
232	UnsupportedDatabase(String),
233
234	/// Duplicate operations detected
235	///
236	/// This error occurs when a new migration has identical operations
237	/// to an existing migration, which usually indicates a problem with
238	/// from_state construction during makemigrations.
239	#[error("Duplicate operations: {0}")]
240	DuplicateOperations(String),
241
242	/// Foreign key integrity violation during table recreation
243	///
244	/// This error occurs when SQLite table recreation results in orphaned
245	/// foreign key references, indicating data integrity issues that must
246	/// be resolved before the migration can proceed.
247	#[error("Foreign key violation: {0}")]
248	ForeignKeyViolation(String),
249
250	/// Path traversal attempt detected in migration path components
251	///
252	/// This error occurs when an app label or migration name contains
253	/// path traversal sequences (e.g., `..`) that could escape the
254	/// migration root directory.
255	#[error("Path traversal detected: {0}")]
256	PathTraversal(String),
257}
258
259pub type Result<T> = std::result::Result<T, MigrationError>;
260
261// Prelude for migrations
262pub mod prelude {
263	pub use super::fields::prelude::*;
264	pub use super::{ColumnDefinition, Constraint, ForeignKeyAction, Migration, Operation};
265}