import GRDB public enum DatabaseSetup { public static func migrator() -> DatabaseMigrator { var migrator = DatabaseMigrator() migrator.registerMigration("v1_initial") { db in try db.create(table: "account") { t in t.primaryKey("id", .text) t.column("name", .text).notNull() t.column("email", .text).notNull() t.column("imapHost", .text).notNull() t.column("imapPort", .integer).notNull() } try db.create(table: "mailbox") { t in t.primaryKey("id", .text) t.belongsTo("account", onDelete: .cascade).notNull() t.column("name", .text).notNull() t.column("uidValidity", .integer).notNull() t.column("uidNext", .integer).notNull() } try db.create(table: "message") { t in t.primaryKey("id", .text) t.belongsTo("account", onDelete: .cascade).notNull() t.belongsTo("mailbox", onDelete: .cascade).notNull() t.column("uid", .integer).notNull() t.column("messageId", .text) t.column("inReplyTo", .text) t.column("refs", .text) t.column("subject", .text) t.column("fromAddress", .text) t.column("fromName", .text) t.column("toAddresses", .text) t.column("ccAddresses", .text) t.column("date", .text).notNull() t.column("snippet", .text) t.column("bodyText", .text) t.column("bodyHtml", .text) t.column("isRead", .boolean).notNull().defaults(to: false) t.column("isFlagged", .boolean).notNull().defaults(to: false) t.column("size", .integer).notNull().defaults(to: 0) t.uniqueKey(["mailboxId", "uid"]) } try db.create(table: "thread") { t in t.primaryKey("id", .text) t.belongsTo("account", onDelete: .cascade).notNull() t.column("subject", .text) t.column("lastDate", .text).notNull() t.column("messageCount", .integer).notNull().defaults(to: 0) } try db.create(table: "threadMessage") { t in t.belongsTo("thread", onDelete: .cascade).notNull() t.belongsTo("message", onDelete: .cascade).notNull() t.primaryKey(["threadId", "messageId"]) } try db.create(table: "attachment") { t in t.primaryKey("id", .text) t.belongsTo("message", onDelete: .cascade).notNull() t.column("filename", .text) t.column("mimeType", .text).notNull() t.column("size", .integer).notNull().defaults(to: 0) t.column("contentId", .text) t.column("cachePath", .text) } try db.create(index: "idx_message_mailbox_uid", on: "message", columns: ["mailboxId", "uid"]) try db.create(index: "idx_message_messageId", on: "message", columns: ["messageId"]) try db.create(index: "idx_thread_lastDate", on: "thread", columns: ["lastDate"]) } migrator.registerMigration("v1_fts5") { db in try db.create(virtualTable: "messageFts", using: FTS5()) { t in t.synchronize(withTable: "message") t.tokenizer = .porter(wrapping: .unicode61()) t.column("subject") t.column("fromName") t.column("fromAddress") t.column("bodyText") } } return migrator } public static func openDatabase(atPath path: String) throws -> DatabasePool { let pool = try DatabasePool(path: path) try migrator().migrate(pool) return pool } public static func openInMemoryDatabase() throws -> DatabaseQueue { let queue = try DatabaseQueue() try migrator().migrate(queue) return queue } }