@@ -188,13 +188,102 @@ internal class SQLiteDatabase: SQLDatabase {
188188 }
189189}
190190
191+ // MARK: - Backups
192+
193+ extension SQLiteDatabase {
194+
195+ /// Store a backup of the contents of the current database to a new SQLite DB file at path.
196+ func storeBackup( to path: String , vacuum: Bool ) throws {
197+
198+ if vacuum {
199+ // This is slower than the backup API, but results in smaller file.
200+ if FileManager . default. fileExists ( atPath: path) {
201+ // File must be removed first if it exists.
202+ try FileManager . default. removeItem ( atPath: path)
203+ }
204+ try execute ( sql: " VACUUM main INTO ?; " , values: . text( path) )
205+ } else {
206+ // Use backup API. This is faster, but the file is bigger.
207+ var backupDB : SQLDatabaseID ? = nil
208+
209+ let result = sqlite3_open ( path, & backupDB)
210+
211+ guard result == SQLITE_OK, let backupDB else {
212+ var message : String = " Failed to open backup database at \( path) "
213+ if let backupDB {
214+ message = String ( cString: sqlite3_errmsg ( backupDB) )
215+ sqlite3_close ( backupDB)
216+ }
217+ throw SQLError . failedToOpenDatabase ( code: result, message: message)
218+ }
219+
220+ let backup = sqlite3_backup_init ( backupDB, " main " , db, " main " )
221+ if backup != nil {
222+ sqlite3_backup_step ( backup, - 1 )
223+ sqlite3_backup_finish ( backup)
224+
225+ }
226+ let backupResult = sqlite3_errcode ( backupDB)
227+ guard backupResult == SQLITE_OK else {
228+ var message : String = " Failed to make backup "
229+ if let cMessage = sqlite3_errmsg ( backupDB) {
230+ message = String ( cString: cMessage)
231+ }
232+ sqlite3_close ( backupDB)
233+ throw SQLError . failedToOpenDatabase ( code: backupResult, message: message)
234+ }
235+
236+ sqlite3_close ( backupDB)
237+ }
238+ }
239+
240+ func restoreBackup( from path: String ) throws {
241+ var backupDB : SQLDatabaseID ? = nil
242+
243+ let result = sqlite3_open ( path, & backupDB)
244+
245+ guard result == SQLITE_OK, let backupDB else {
246+ var message : String = " Failed to open backup database at \( path) "
247+ if let backupDB {
248+ if let cMessage = sqlite3_errmsg ( backupDB) {
249+ message = String ( cString: cMessage)
250+ }
251+ sqlite3_close ( backupDB)
252+ }
253+ throw SQLError . failedToOpenDatabase ( code: result, message: message)
254+ }
255+
256+ let backup = sqlite3_backup_init ( db, " main " , backupDB, " main " )
257+ if backup != nil {
258+ sqlite3_backup_step ( backup, - 1 )
259+ sqlite3_backup_finish ( backup)
260+
261+ }
262+ let backupResult = sqlite3_errcode ( db)
263+ guard backupResult == SQLITE_OK else {
264+ var message : String = " Failed to make backup "
265+ if let cMessage = sqlite3_errmsg ( db) {
266+ message = String ( cString: cMessage)
267+ }
268+ sqlite3_close ( backupDB)
269+ throw SQLError . failedToOpenDatabase ( code: backupResult, message: message)
270+ }
271+
272+ sqlite3_close ( backupDB)
273+ }
274+ }
275+
276+ // MARK: - SQLErrorMessage
277+
191278extension SQLiteDatabase : SQLErrorMessage {
192279
193280 var current : String {
194281 return errorMessage
195282 }
196283}
197284
285+ // MARK: - UpdateHook
286+
198287private class UpdateHook {
199288 let hook : ( SQLUpdateType , _ table: String ? , _ rowID: Int64 ) -> Void
200289
0 commit comments