87 lines
3.0 KiB
Swift
87 lines
3.0 KiB
Swift
/// An internal struct that defines a migration.
|
|
struct Migration {
|
|
enum ForeignKeyChecks {
|
|
case deferred
|
|
case immediate
|
|
case disabled
|
|
}
|
|
|
|
let identifier: String
|
|
var foreignKeyChecks: ForeignKeyChecks
|
|
let migrate: (Database) throws -> Void
|
|
|
|
func run(_ db: Database) throws {
|
|
if try Bool.fetchOne(db, sql: "PRAGMA foreign_keys") ?? false {
|
|
switch foreignKeyChecks {
|
|
case .deferred:
|
|
try runWithDeferredForeignKeysChecks(db)
|
|
case .immediate:
|
|
try runWithImmediateForeignKeysChecks(db)
|
|
case .disabled:
|
|
try runWithDisabledForeignKeysChecks(db)
|
|
}
|
|
} else {
|
|
try runWithImmediateForeignKeysChecks(db)
|
|
}
|
|
}
|
|
|
|
private func runWithImmediateForeignKeysChecks(_ db: Database) throws {
|
|
try db.inTransaction(.immediate) {
|
|
try migrate(db)
|
|
try insertAppliedIdentifier(db)
|
|
return .commit
|
|
}
|
|
}
|
|
|
|
private func runWithDisabledForeignKeysChecks(_ db: Database) throws {
|
|
try db.execute(sql: "PRAGMA foreign_keys = OFF")
|
|
try throwingFirstError(
|
|
execute: {
|
|
try db.inTransaction(.immediate) {
|
|
try migrate(db)
|
|
try insertAppliedIdentifier(db)
|
|
return .commit
|
|
}
|
|
},
|
|
finally: {
|
|
try db.execute(sql: "PRAGMA foreign_keys = ON")
|
|
})
|
|
}
|
|
|
|
private func runWithDeferredForeignKeysChecks(_ db: Database) throws {
|
|
// Support for database alterations described at
|
|
// https://www.sqlite.org/lang_altertable.html#otheralter
|
|
//
|
|
// > 1. If foreign key constraints are enabled, disable them using
|
|
// > PRAGMA foreign_keys=OFF.
|
|
try db.execute(sql: "PRAGMA foreign_keys = OFF")
|
|
|
|
try throwingFirstError(
|
|
execute: {
|
|
// > 2. Start a transaction.
|
|
try db.inTransaction(.immediate) {
|
|
try migrate(db)
|
|
try insertAppliedIdentifier(db)
|
|
|
|
// > 10. If foreign key constraints were originally enabled
|
|
// > then run PRAGMA foreign_key_check to verify that the
|
|
// > schema change did not break any foreign
|
|
// > key constraints.
|
|
try db.checkForeignKeys()
|
|
|
|
// > 11. Commit the transaction started in step 2.
|
|
return .commit
|
|
}
|
|
},
|
|
finally: {
|
|
// > 12. If foreign keys constraints were originally enabled,
|
|
// > reenable them now.
|
|
try db.execute(sql: "PRAGMA foreign_keys = ON")
|
|
})
|
|
}
|
|
|
|
private func insertAppliedIdentifier(_ db: Database) throws {
|
|
try db.execute(sql: "INSERT INTO grdb_migrations (identifier) VALUES (?)", arguments: [identifier])
|
|
}
|
|
}
|