Migrator

Struct Migrator 

Source
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 connection
  • tasks - Queue of table creation tasks
  • fk_task - Queue of foreign key assignment tasks

§Lifecycle

  1. Create migrator via Database::migrator()
  2. Register models via register::<T>()
  3. 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>

Source

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);
Source

pub fn register<T>(self) -> Self
where T: Model + 'static + Send + Sync,

Registers a Model for migration.

This method queues two tasks for the specified model:

  1. Table Creation Task: Creates the table with columns, indexes, and inline constraints (PRIMARY KEY, UNIQUE, NOT NULL, etc.)
  2. 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 implement Model + 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
  1. User table creation
  2. Post table creation
  3. Comment table creation
  4. Post foreign keys (user_id → User.id)
  5. Comment foreign keys (post_id → Post.id, user_id → User.id)
§See Also
Source

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 EXISTS for 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 success
  • Err(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

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> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more