Backport the alleged sqlite deadlock workaround

This commit is contained in:
Ali 2021-04-06 02:57:11 +04:00
parent 0dca1b727d
commit ec8870816d

View File

@ -65,7 +65,7 @@ struct SqlitePreparedStatement {
}
return res == SQLITE_ROW
}
struct SqlError: Error {
var code: Int32
}
@ -234,10 +234,8 @@ public final class SqliteValueBox: ValueBox {
let _ = try? FileManager.default.createDirectory(atPath: basePath, withIntermediateDirectories: true, attributes: nil)
let path = basePath + "/db_sqlite"
#if DEBUG
print("Instance \(self) opening sqlite at \(path)")
#endif
postboxLog("Instance \(self) opening sqlite at \(path)")
#if DEBUG
let exists = FileManager.default.fileExists(atPath: path)
@ -297,8 +295,10 @@ public final class SqliteValueBox: ValueBox {
let _ = try? FileManager.default.removeItem(atPath: path)
preconditionFailure("Couldn't open database")
}
sqlite3_busy_timeout(database.handle, 1000 * 10000)
postboxLog("Did open DB at \(path)")
sqlite3_busy_timeout(database.handle, 5 * 1000)
var resultCode: Bool = true
@ -306,8 +306,12 @@ public final class SqliteValueBox: ValueBox {
assert(resultCode)
resultCode = database.execute("PRAGMA cipher_default_plaintext_header_size=32")
assert(resultCode)
postboxLog("Did set up cipher")
if self.isEncrypted(database) {
postboxLog("Database is encrypted")
if let encryptionParameters = encryptionParameters {
precondition(encryptionParameters.salt.data.count == 16)
precondition(encryptionParameters.key.data.count == 32)
@ -316,12 +320,15 @@ public final class SqliteValueBox: ValueBox {
resultCode = database.execute("PRAGMA key=\"x'\(hexKey)'\"")
assert(resultCode)
postboxLog("Setting encryption key")
if self.isEncrypted(database) {
postboxLog("Encryption key is invalid")
if isTemporary || isReadOnly {
return nil
}
postboxLog("Encryption key is invalid")
for fileName in dabaseFileNames {
let _ = try? FileManager.default.removeItem(atPath: basePath + "/\(fileName)")
@ -354,6 +361,8 @@ public final class SqliteValueBox: ValueBox {
assert(resultCode)
}
} else if let encryptionParameters = encryptionParameters, encryptionParameters.forceEncryptionIfNoSet {
postboxLog("Not encrypted")
let hexKey = hexString(encryptionParameters.key.data + encryptionParameters.salt.data)
if FileManager.default.fileExists(atPath: path) {
@ -409,6 +418,8 @@ public final class SqliteValueBox: ValueBox {
}
}
}
postboxLog("Did set up encryption")
//database.execute("PRAGMA cache_size=-2097152")
resultCode = database.execute("PRAGMA mmap_size=0")
@ -421,6 +432,9 @@ public final class SqliteValueBox: ValueBox {
assert(resultCode)
resultCode = database.execute("PRAGMA cipher_memory_security = OFF")
assert(resultCode)
postboxLog("Did set up pragmas")
//resultCode = database.execute("PRAGMA wal_autocheckpoint=500")
//database.execute("PRAGMA journal_size_limit=1536")
@ -441,8 +455,12 @@ public final class SqliteValueBox: ValueBox {
let _ = self.runPragma(database, "checkpoint_fullfsync = 1")
assert(self.runPragma(database, "checkpoint_fullfsync") == "1")
postboxLog("Did set up checkpoint_fullfsync")
self.beginInternal(database: database)
postboxLog("Did begin transaction")
let result = self.getUserVersion(database)
@ -462,8 +480,12 @@ public final class SqliteValueBox: ValueBox {
for table in self.listFullTextTables(database) {
self.fullTextTables[table.id] = table
}
postboxLog("Did load tables")
self.commitInternal(database: database)
postboxLog("Did commit final")
lock.unlock()
@ -518,7 +540,21 @@ public final class SqliteValueBox: ValueBox {
private func isEncrypted(_ database: Database) -> Bool {
var statement: OpaquePointer? = nil
postboxLog("isEncrypted prepare...")
let allIsOk = Atomic<Bool>(value: false)
let databasePath = self.databasePath
DispatchQueue.global().asyncAfter(deadline: .now() + 5.0, execute: {
if allIsOk.with({ $0 }) == false {
postboxLog("Timeout reached, discarding database")
try? FileManager.default.removeItem(atPath: databasePath)
preconditionFailure()
}
})
let status = sqlite3_prepare_v2(database.handle, "SELECT * FROM sqlite_master LIMIT 1", -1, &statement, nil)
let _ = allIsOk.swap(true)
postboxLog("isEncrypted prepare done")
if statement == nil {
postboxLog("isEncrypted: sqlite3_prepare_v2 status = \(status) [\(self.databasePath)]")
return true
@ -536,6 +572,7 @@ public final class SqliteValueBox: ValueBox {
preparedStatement.destroy()
return true
}
postboxLog("isEncrypted step done")
preparedStatement.destroy()
return status == SQLITE_NOTADB
}