DatabaseMigrator

public struct DatabaseMigrator

A DatabaseMigrator registers and applies database migrations.

Migrations are named blocks of SQL statements that are guaranteed to be applied in order, once and only once.

When a user upgrades your application, only non-applied migration are run.

Usage:

var migrator = DatabaseMigrator()

// 1st migration
migrator.registerMigration("createLibrary") { db in
    try db.create(table: "author") { t in
        t.autoIncrementedPrimaryKey("id")
        t.column("creationDate", .datetime)
        t.column("name", .text).notNull()
    }

    try db.create(table: "book") { t in
        t.autoIncrementedPrimaryKey("id")
        t.column("authorId", .integer)
            .notNull()
            .references("author", onDelete: .cascade)
        t.column("title", .text).notNull()
    }
}

// 2nd migration
migrator.registerMigration("AddBirthYearToAuthors") { db in
    try db.alter(table: "author") { t
        t.add(column: "birthYear", .integer)
    }
}

// Migrations for future versions will be inserted here:
//
// // 3rd migration
// migrator.registerMigration("...") { db in
//     ...
// }

try migrator.migrate(dbQueue)
  • Controls how migrations handle foreign keys constraints.

    See more

    Declaration

    Swift

    public enum ForeignKeyChecks
  • When the eraseDatabaseOnSchemaChange flag is true, the migrator will automatically wipe out the full database content, and recreate the whole database from scratch, if it detects that a migration has changed its definition.

    This flag can destroy your precious users’ data!

    But it may be useful in two situations:

    1. During application development, as you are still designing migrations, and the schema changes often.

      In this case, it is recommended that you make sure this flag does not ship in the distributed application, in order to avoid undesired data loss:

      var migrator = DatabaseMigrator()
      #if DEBUG
      // Speed up development by nuking the database when migrations change
      migrator.eraseDatabaseOnSchemaChange = true
      #endif
      
    2. When the database content can easily be recreated, such as a cache for some downloaded data.

    Declaration

    Swift

    public var eraseDatabaseOnSchemaChange: Bool
  • A new migrator.

    Declaration

    Swift

    public init()

Disabling Foreign Key Checks

  • Returns a migrator that will not perform deferred foreign key checks in all newly registered migrations.

    The returned migrator is unsafe, because it no longer guarantees the integrity of the database. It is your responsibility to register migrations that do not break foreign key constraints.

    Running migrations without foreign key checks can improve migration performance on huge databases.

    Example:

    var migrator = DatabaseMigrator()
    migrator.registerMigration("A") { db in
        // Runs with deferred foreign key checks
    }
    migrator.registerMigration("B", foreignKeyChecks: .immediate) { db in
        // Runs with immediate foreign key checks
    }
    
    migrator = migrator.disablingDeferredForeignKeyChecks()
    migrator.registerMigration("C") { db in
        // Runs with disabled foreign key checks
    }
    migrator.registerMigration("D", foreignKeyChecks: .immediate) { db in
        // Runs with immediate foreign key checks
    }
    

    Warning

    Before using this unsafe method, try to run your migrations with .immediate foreign key checks, if possible. This may enhance migration performances, while preserving the database integrity guarantee.

    Declaration

    Swift

    public func disablingDeferredForeignKeyChecks() -> DatabaseMigrator

Registering Migrations

  • Registers a migration.

    migrator.registerMigration("createAuthors") { db in
        try db.create(table: "author") { t in
            t.autoIncrementedPrimaryKey("id")
            t.column("creationDate", .datetime)
            t.column("name", .text).notNull()
        }
    }
    

    Precondition

    No migration with the same same as already been registered.

    Declaration

    Swift

    public mutating func registerMigration(
        _ identifier: String,
        foreignKeyChecks: ForeignKeyChecks = .deferred,
        migrate: @escaping (Database) throws -> Void)

    Parameters

    identifier

    The migration identifier.

    foreignKeyChecks

    This parameter is ignored if the database has not enabled foreign keys.

    block

    The migration block that performs SQL statements.

Applying Migrations

  • Iterate migrations in the same order as they were registered. If a migration has not yet been applied, its block is executed in a transaction.

    Throws

    An eventual error thrown by the registered migration blocks.

    Declaration

    Swift

    public func migrate(_ writer: DatabaseWriter) throws

    Parameters

    writer

    A DatabaseWriter (DatabaseQueue or DatabasePool) where migrations should apply.

  • Iterate migrations in the same order as they were registered, up to the provided target. If a migration has not yet been applied, its block is executed in a transaction.

    Throws

    An eventual error thrown by the registered migration blocks.

    Declaration

    Swift

    public func migrate(_ writer: DatabaseWriter, upTo targetIdentifier: String) throws

    Parameters

    writer

    A DatabaseWriter (DatabaseQueue or DatabasePool) where migrations should apply.

    targetIdentifier

    The identifier of a registered migration.

  • Iterate migrations in the same order as they were registered. If a migration has not yet been applied, its block is executed in a transaction.

    Declaration

    Swift

    public func asyncMigrate(
        _ writer: DatabaseWriter,
        completion: @escaping (Database, Error?) -> Void)

    Parameters

    writer

    A DatabaseWriter (DatabaseQueue or DatabasePool) where migrations should apply.

    completion

    A closure that is called in a protected dispatch queue that can write in the database, with the eventual migration error.

Querying Migrations

  • The list of registered migration identifiers, in the same order as they have been registered.

    Declaration

    Swift

    public var migrations: [String] { get }
  • Returns the identifiers of registered and applied migrations, in the order of registration.

    See also appliedIdentifiers(_:).

    Throws

    An eventual database error.

    Declaration

    Swift

    public func appliedMigrations(_ db: Database) throws -> [String]

    Parameters

    db

    A database connection.

  • Returns the applied migration identifiers, even unregistered ones.

    See also appliedMigrations(_:).

    Throws

    An eventual database error.

    Declaration

    Swift

    public func appliedIdentifiers(_ db: Database) throws -> Set<String>

    Parameters

    db

    A database connection.

  • Returns the identifiers of completed migrations, of which all previous migrations have been applied.

    Throws

    An eventual database error.

    Declaration

    Swift

    public func completedMigrations(_ db: Database) throws -> [String]

    Parameters

    db

    A database connection.

  • Returns true if all migrations are applied.

    Throws

    An eventual database error.

    Declaration

    Swift

    public func hasCompletedMigrations(_ db: Database) throws -> Bool

    Parameters

    db

    A database connection.

  • Returns whether database contains unknown migration identifiers, which is likely the sign that the database has migrated further than the migrator itself supports.

    Throws

    An eventual database error.

    Declaration

    Swift

    public func hasBeenSuperseded(_ db: Database) throws -> Bool

    Parameters

    db

    A database connection.

Publishing Migrations

  • Returns a Publisher that asynchronously migrates a database.

    let migrator: DatabaseMigrator = ...
    let dbQueue: DatabaseQueue = ...
    let publisher = migrator.migratePublisher(dbQueue)
    

    It completes on the main dispatch queue.

    Declaration

    Swift

    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
    public func migratePublisher(_ writer: DatabaseWriter) -> DatabasePublishers.Migrate

    Parameters

    writer

    A DatabaseWriter (DatabaseQueue or DatabasePool) where migrations should apply.

  • Returns a Publisher that asynchronously migrates a database.

    let migrator: DatabaseMigrator = ...
    let dbQueue: DatabaseQueue = ...
    let publisher = migrator.migratePublisher(dbQueue, receiveOn: DispatchQueue.global())
    

    It completes on scheduler.

    Declaration

    Swift

    @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
    public func migratePublisher<S>(_ writer: DatabaseWriter, receiveOn scheduler: S)
    -> DatabasePublishers.Migrate
    where S: Scheduler

    Parameters

    writer

    A DatabaseWriter (DatabaseQueue or DatabasePool) where migrations should apply.

    scheduler

    A Combine Scheduler.