@@ -111,7 +111,7 @@ public struct DatabaseMigrator: Sendable {
111111 /// See also ``hasSchemaChanges(_:)``.
112112 public var eraseDatabaseOnSchemaChange = false
113113 private var defersForeignKeyChecks = true
114- private var _migrations : [ Migration ] = [ ]
114+ private var _migrations : OrderedDictionary < String , Migration > = [ : ]
115115
116116 /// A new migrator.
117117 public init ( ) {
@@ -349,10 +349,10 @@ public struct DatabaseMigrator: Sendable {
349349 /// - parameter writer: A DatabaseWriter.
350350 /// - throws: The error thrown by the first failed migration.
351351 public func migrate( _ writer: any DatabaseWriter ) throws {
352- guard let lastMigration = _migrations. last else {
352+ guard let lastMigrationIdentifier = _migrations. keys . last else {
353353 return
354354 }
355- try migrate ( writer, upTo: lastMigration . identifier )
355+ try migrate ( writer, upTo: lastMigrationIdentifier )
356356 }
357357
358358 /// Runs all unapplied migrations, in the same order as they
@@ -387,8 +387,8 @@ public struct DatabaseMigrator: Sendable {
387387 writer. asyncBarrierWriteWithoutTransaction { dbResult in
388388 do {
389389 let db = try dbResult. get ( )
390- if let lastMigration = _migrations. last {
391- try migrate ( db, upTo: lastMigration . identifier )
390+ if let lastMigrationIdentifier = _migrations. keys . last {
391+ try migrate ( db, upTo: lastMigrationIdentifier )
392392 }
393393 completion ( . success( db) )
394394 } catch {
@@ -426,14 +426,13 @@ public struct DatabaseMigrator: Sendable {
426426 ///
427427 public func hasSchemaChanges( _ db: Database ) throws -> Bool {
428428 let appliedIdentifiers = try appliedIdentifiers ( db)
429- let knownIdentifiers = Set ( _migrations. map { $0 . identifier } )
429+ let knownIdentifiers = Set ( _migrations. keys )
430430 if !appliedIdentifiers. isSubset ( of: knownIdentifiers) {
431431 // Database contains an unknown migration
432432 return true
433433 }
434434
435- if let lastAppliedIdentifier = _migrations
436- . map ( \. identifier)
435+ if let lastAppliedIdentifier = _migrations. keys
437436 . last ( where: { appliedIdentifiers. contains ( $0) } )
438437 {
439438 // Some migrations were already applied.
@@ -492,7 +491,7 @@ public struct DatabaseMigrator: Sendable {
492491 /// The list of registered migration identifiers, in the same order as they
493492 /// have been registered.
494493 public var migrations : [ String ] {
495- _migrations. map ( \ . identifier )
494+ _migrations. keys
496495 }
497496
498497 /// Returns the identifiers of registered and applied migrations, in the
@@ -502,7 +501,7 @@ public struct DatabaseMigrator: Sendable {
502501 /// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
503502 public func appliedMigrations( _ db: Database ) throws -> [ String ] {
504503 let appliedIdentifiers = try self . appliedIdentifiers ( db)
505- return _migrations. map { $0 . identifier } . filter { appliedIdentifiers. contains ( $0) }
504+ return _migrations. keys . filter { appliedIdentifiers. contains ( $0) }
506505 }
507506
508507 /// Returns the applied migration identifiers, even unregistered ones.
@@ -531,7 +530,7 @@ public struct DatabaseMigrator: Sendable {
531530 /// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
532531 public func completedMigrations( _ db: Database ) throws -> [ String ] {
533532 let appliedIdentifiers = try appliedMigrations ( db)
534- let knownIdentifiers = _migrations. map ( \ . identifier )
533+ let knownIdentifiers = _migrations. keys
535534 return zip ( appliedIdentifiers, knownIdentifiers)
536535 . prefix ( while: { ( applied: String , known: String ) in applied == known } )
537536 . map { $0. 0 }
@@ -543,7 +542,7 @@ public struct DatabaseMigrator: Sendable {
543542 /// - parameter db: A database connection.
544543 /// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
545544 public func hasCompletedMigrations( _ db: Database ) throws -> Bool {
546- try completedMigrations ( db) . last == _migrations. last ? . identifier
545+ try completedMigrations ( db) . last == _migrations. keys . last
547546 }
548547
549548 /// A boolean value indicating whether the database refers to
@@ -556,7 +555,7 @@ public struct DatabaseMigrator: Sendable {
556555 /// - throws: A ``DatabaseError`` whenever an SQLite error occurs.
557556 public func hasBeenSuperseded( _ db: Database ) throws -> Bool {
558557 let appliedIdentifiers = try self . appliedIdentifiers ( db)
559- let knownIdentifiers = _migrations. map ( \ . identifier )
558+ let knownIdentifiers = _migrations. keys
560559 return appliedIdentifiers. contains { !knownIdentifiers. contains ( $0) }
561560 }
562561
@@ -574,51 +573,53 @@ public struct DatabaseMigrator: Sendable {
574573
575574 private mutating func registerMigration( _ migration: Migration ) {
576575 GRDBPrecondition (
577- ! _migrations. map ( { $0 . identifier } ) . contains ( migration. identifier) ,
576+ _migrations [ migration. identifier] == nil ,
578577 " already registered migration: \( String ( reflecting: migration. identifier) ) " )
579- _migrations. append ( migration)
578+ _migrations. appendValue ( migration, forKey : migration . identifier )
580579 }
581580
582581 /// Returns unapplied migration executions
583582 private func unappliedExecutions( upTo targetIdentifier: String , appliedIdentifiers: Set < String > ) -> [ Execution ] {
584- var expectedMigrations : [ Migration ] = [ ]
585- for migration in _migrations {
586- expectedMigrations. append ( migration)
587- if migration. identifier == targetIdentifier {
588- break
589- }
590- }
591-
592- // targetIdentifier must refer to a registered migration
593- GRDBPrecondition (
594- expectedMigrations. last? . identifier == targetIdentifier,
595- " undefined migration: \( String ( reflecting: targetIdentifier) ) " )
583+ var executions : [ Execution ] = [ ]
584+ var foundTarget = false
596585
597- return expectedMigrations. compactMap { migration in
598- if appliedIdentifiers. contains ( migration. identifier) {
599- if migration. mergedIdentifiers. isDisjoint ( with: appliedIdentifiers) {
600- // Nothing to do
601- return nil
602- } else {
586+ for (identifier, migration) in _migrations {
587+ if appliedIdentifiers. contains ( identifier) {
588+ if !migration. mergedIdentifiers. isDisjoint ( with: appliedIdentifiers) {
603589 // Migration is applied, but we have some merged identifiers to delete
604- return Execution ( migration: migration, mode: . deleteMergedIdentifiers)
590+ executions . append ( Execution ( migration: migration, mode: . deleteMergedIdentifiers) )
605591 }
606592 } else {
607593 // Migration is not applied yet.
608594 let appliedMergedIdentifiers = migration. mergedIdentifiers. intersection ( appliedIdentifiers)
609- return Execution ( migration: migration, mode: . run( mergedIdentifiers: appliedMergedIdentifiers) )
595+ executions. append ( Execution (
596+ migration: migration,
597+ mode: . run( mergedIdentifiers: appliedMergedIdentifiers)
598+ ) )
599+ }
600+
601+ if identifier == targetIdentifier {
602+ foundTarget = true
603+ break
610604 }
611605 }
606+
607+ // targetIdentifier must refer to a registered migration
608+ GRDBPrecondition (
609+ foundTarget,
610+ " undefined migration: \( String ( reflecting: targetIdentifier) ) " )
611+
612+ return executions
612613 }
613614
614615 private func runMigrations( _ db: Database , upTo targetIdentifier: String ) throws {
615616 try db. execute ( sql: " CREATE TABLE IF NOT EXISTS grdb_migrations (identifier TEXT NOT NULL PRIMARY KEY) " )
616617
617618 // Subsequent migration must not be applied
618619 let appliedMigrations = try self . appliedMigrations ( db) // Only known ids
619- if let targetIndex = _migrations. firstIndex ( where : { $0 . identifier == targetIdentifier } ) ,
620+ if let targetIndex = _migrations. keys . firstIndex ( of : targetIdentifier) ,
620621 let lastAppliedMigration = appliedMigrations. last,
621- let lastAppliedIndex = _migrations. firstIndex ( where : { $0 . identifier == lastAppliedMigration } ) ,
622+ let lastAppliedIndex = _migrations. keys . firstIndex ( of : lastAppliedMigration) ,
622623 targetIndex < lastAppliedIndex
623624 {
624625 fatalError ( " database is already migrated beyond migration \( String ( reflecting: targetIdentifier) ) " )
0 commit comments