pub struct Migrator<'a> { /* private fields */ }Expand description
Schema migration manager.
The Migrator is responsible for managing and executing database schema migrations.
It maintains two separate task queues: one for table creation and one for foreign
key assignment. This separation ensures that all tables exist before any foreign
keys are created.
§Fields
db- Reference to the database connectiontasks- Queue of table creation tasksfk_task- Queue of foreign key assignment tasks
§Lifecycle
- Create migrator via
Database::migrator() - Register models via
register::<T>() - Execute all migrations via
run()
§Example
use bottle_orm::{Database, Model};
#[derive(Model)]
struct User {
#[orm(primary_key)]
id: i32,
username: String,
}
#[derive(Model)]
struct Post {
#[orm(primary_key)]
id: i32,
#[orm(foreign_key = "User::id")]
user_id: i32,
title: String,
}
let db = Database::connect("sqlite::memory:").await?;
let result = db.migrator()
.register::<User>()
.register::<Post>()
.run()
.await?;Implementations§
Source§impl<'a> Migrator<'a>
impl<'a> Migrator<'a>
Sourcepub fn new(db: &'a Database) -> Self
pub fn new(db: &'a Database) -> Self
Creates a new Migrator instance associated with a Database.
This constructor initializes empty task queues for table creation
and foreign key assignment. Typically called via Database::migrator()
rather than directly.
§Arguments
db- Reference to the database connection
§Returns
A new Migrator instance with empty task queues
§Example
// Usually called via database method
let migrator = db.migrator();
// Direct construction (rarely needed)
let migrator = Migrator::new(&db);Sourcepub fn register<T>(self) -> Self
pub fn register<T>(self) -> Self
Registers a Model for migration.
This method queues two tasks for the specified model:
- Table Creation Task: Creates the table with columns, indexes, and inline constraints (PRIMARY KEY, UNIQUE, NOT NULL, etc.)
- Foreign Key Task: Assigns foreign key constraints after all tables are created
Multiple models can be registered by chaining calls to this method. The tasks will be executed in the order they were registered.
§Type Parameters
T- The Model type to register. Must implementModel + Send + Sync + 'static
§Returns
Returns self to enable method chaining
§Example
use bottle_orm::{Database, Model};
use uuid::Uuid;
#[derive(Model)]
struct User {
#[orm(primary_key)]
id: Uuid,
username: String,
}
#[derive(Model)]
struct Post {
#[orm(primary_key)]
id: Uuid,
#[orm(foreign_key = "User::id")]
user_id: Uuid,
title: String,
}
#[derive(Model)]
struct Comment {
#[orm(primary_key)]
id: Uuid,
#[orm(foreign_key = "Post::id")]
post_id: Uuid,
#[orm(foreign_key = "User::id")]
user_id: Uuid,
content: String,
}
// Register multiple models
db.migrator()
.register::<User>() // Creates 'user' table first
.register::<Post>() // Creates 'post' table
.register::<Comment>() // Creates 'comment' table
.run() // Executes all migrations
.await?;§Task Execution Order
- User table creation
- Post table creation
- Comment table creation
- Post foreign keys (user_id → User.id)
- Comment foreign keys (post_id → Post.id, user_id → User.id)
§See Also
run()- For executing registered migrationsDatabase::create_table()- For manual table creationDatabase::assign_foreign_keys()- For manual FK assignment
Sourcepub async fn run(self) -> Result<Database, Error>
pub async fn run(self) -> Result<Database, Error>
Executes all registered migration tasks.
This method runs all queued migrations in two phases:
Phase 1: Table Creation
- Executes all table creation tasks in registration order
- Creates tables with columns, indexes, and inline constraints
- Uses
CREATE TABLE IF NOT EXISTSfor idempotency
Phase 2: Foreign Key Assignment
- Executes all foreign key tasks in registration order
- Creates foreign key constraints between tables
- Checks for existing constraints to avoid duplicates
If any task fails, the entire migration is aborted and an error is returned.
§Returns
Ok(Database)- Cloned database instance on successErr(sqlx::Error)- Database error during migration
§Error Handling
Errors can occur for various reasons:
- Connection Errors: Database connection lost during migration
- Syntax Errors: Invalid SQL generated (shouldn’t happen with correct Model definitions)
- Permission Errors: Insufficient database privileges
- Constraint Violations: Existing data violates new constraints
§Example
use bottle_orm::{Database, Model};
#[derive(Model)]
struct User {
#[orm(primary_key)]
id: i32,
username: String,
}
let db = Database::connect("sqlite::memory:").await?;
// Run migrations
match db.migrator().register::<User>().run().await {
Ok(db) => println!("Migrations completed successfully"),
Err(e) => eprintln!("Migration failed: {}", e),
}§Idempotency
Migrations are designed to be idempotent and can be run multiple times safely:
// First run: creates tables
db.migrator().register::<User>().run().await?;
// Second run: no-op (tables already exist)
db.migrator().register::<User>().run().await?;§Performance Considerations
- Migrations are executed sequentially, not in parallel
- Large schemas may take time to migrate
- Consider running migrations during deployment/startup
- Use database transactions where supported
§See Also
register()- For registering modelsDatabase::create_table()- For manual table creationDatabase::assign_foreign_keys()- For manual FK assignment
Auto Trait Implementations§
impl<'a> Freeze for Migrator<'a>
impl<'a> !RefUnwindSafe for Migrator<'a>
impl<'a> Send for Migrator<'a>
impl<'a> Sync for Migrator<'a>
impl<'a> Unpin for Migrator<'a>
impl<'a> !UnwindSafe for Migrator<'a>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more