diff --git a/Postbox/Coding.swift b/Postbox/Coding.swift index d5acbb6d98..a2a4f9779a 100644 --- a/Postbox/Coding.swift +++ b/Postbox/Coding.swift @@ -252,6 +252,15 @@ public final class Encoder { self.buffer.write(key.utf8Start, offset: 0, length: Int(length)) } + public func encodeKey(_ key: String) { + let data = key.data(using: .utf8)! + data.withUnsafeBytes { (keyBytes: UnsafePointer) -> Void in + var length: Int8 = Int8(data.count) + self.buffer.write(&length, offset: 0, length: 1) + self.buffer.write(keyBytes, offset: 0, length: Int(length)) + } + } + public func encodeNil(forKey key: StaticString) { self.encodeKey(key) var type: Int8 = ValueType.Nil.rawValue @@ -266,6 +275,14 @@ public final class Encoder { self.buffer.write(&v, offset: 0, length: 4) } + public func encodeInt32(_ value: Int32, forKey key: String) { + self.encodeKey(key) + var type: Int8 = ValueType.Int32.rawValue + self.buffer.write(&type, offset: 0, length: 1) + var v = value + self.buffer.write(&v, offset: 0, length: 4) + } + public func encodeInt64(_ value: Int64, forKey key: StaticString) { self.encodeKey(key) var type: Int8 = ValueType.Int64.rawValue @@ -325,6 +342,23 @@ public final class Encoder { self.buffer.write(innerEncoder.buffer.memory, offset: 0, length: Int(length)) } + public func encodeObjectWithEncoder(_ value: T, encoder: (Encoder) -> Void, forKey key: String) { + self.encodeKey(key) + var type: Int8 = ValueType.Object.rawValue + self.buffer.write(&type, offset: 0, length: 1) + + let string = "\(type(of: value))" + var typeHash: Int32 = murMurHashString32(string) + self.buffer.write(&typeHash, offset: 0, length: 4) + + let innerEncoder = Encoder() + encoder(innerEncoder) + + var length: Int32 = Int32(innerEncoder.buffer.offset) + self.buffer.write(&length, offset: 0, length: 4) + self.buffer.write(innerEncoder.buffer.memory, offset: 0, length: Int(length)) + } + public func encodeInt32Array(_ value: [Int32], forKey key: StaticString) { self.encodeKey(key) var type: Int8 = ValueType.Int32Array.rawValue @@ -591,6 +625,47 @@ public final class Decoder { return false } + + private class func positionOnStringKey(_ rawBytes: UnsafeRawPointer, offset: inout Int, maxOffset: Int, length: Int, key: String, valueType: ValueType) -> Bool + { + let bytes = rawBytes.assumingMemoryBound(to: Int8.self) + + let startOffset = offset + + let keyData = key.data(using: .utf8)! + + return keyData.withUnsafeBytes { (keyBytes: UnsafePointer) -> Bool in + let keyLength: Int = keyData.count + while (offset < maxOffset) { + let readKeyLength = bytes[offset] + assert(readKeyLength >= 0) + offset += 1 + offset += Int(readKeyLength) + + let readValueType = bytes[offset] + offset += 1 + + if keyLength == Int(readKeyLength) && memcmp(bytes + (offset - Int(readKeyLength) - 1), keyBytes, keyLength) == 0 { + if readValueType == valueType.rawValue { + return true + } else if readValueType == ValueType.Nil.rawValue { + return false + } else { + skipValue(bytes, offset: &offset, length: length, valueType: ValueType(rawValue: readValueType)!) + } + } else { + skipValue(bytes, offset: &offset, length: length, valueType: ValueType(rawValue: readValueType)!) + } + } + + if (startOffset != 0) { + offset = 0 + return positionOnStringKey(bytes, offset: &offset, maxOffset: startOffset, length: length, key: key, valueType: valueType) + } + + return false + } + } private class func positionOnKey(_ bytes: UnsafePointer, offset: inout Int, maxOffset: Int, length: Int, key: Int16, valueType: ValueType) -> Bool { @@ -634,6 +709,17 @@ public final class Decoder { } } + public func decodeInt32ForKey(_ key: String) -> Int32 { + if Decoder.positionOnStringKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32) { + var value: Int32 = 0 + memcpy(&value, self.buffer.memory + self.offset, 4) + self.offset += 4 + return value + } else { + return 0 + } + } + public func decodeInt32ForKey(_ key: StaticString) -> Int32? { if Decoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32) { var value: Int32 = 0 @@ -753,6 +839,24 @@ public final class Decoder { } } + public func decodeObjectForKeyThrowing(_ key: StaticString, decoder: (Decoder) throws -> Any) throws -> Any? { + if Decoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Object) { + var typeHash: Int32 = 0 + memcpy(&typeHash, self.buffer.memory + self.offset, 4) + self.offset += 4 + + var length: Int32 = 0 + memcpy(&length, self.buffer.memory + self.offset, 4) + + let innerDecoder = Decoder(buffer: ReadBuffer(memory: self.buffer.memory + (self.offset + 4), length: Int(length), freeWhenDone: false)) + self.offset += 4 + Int(length) + + return try decoder(innerDecoder) + } else { + return nil + } + } + public func decodeInt32ArrayForKey(_ key: StaticString) -> [Int32] { if Decoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32Array) { var length: Int32 = 0 @@ -1023,7 +1127,7 @@ public final class Decoder { } } - public func decodeObjectDictionaryForKey(_ key: StaticString, keyDecoder: (Decoder) -> K) -> [K : V] where K: Hashable { + public func decodeObjectDictionaryForKey(_ key: StaticString, keyDecoder: (Decoder) -> K) -> [K : V] where K: Hashable, K: Hashable { if Decoder.positionOnKey(self.buffer.memory, offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .ObjectDictionary) { var length: Int32 = 0 memcpy(&length, self.buffer.memory + self.offset, 4) diff --git a/Postbox/Database.swift b/Postbox/Database.swift index ba023b6db3..1712061b6d 100644 --- a/Postbox/Database.swift +++ b/Postbox/Database.swift @@ -68,8 +68,15 @@ public final class Database { /// /// - returns: A new database connection. public init?(_ location: Location = .InMemory, readonly: Bool = false) { - let flags = readonly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE - let res = sqlite3_open_v2(location.description, &self.handle, flags | SQLITE_OPEN_FULLMUTEX, nil) + switch location { + case let .URI(uri): + let _ = open(uri + "-guard", O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR) + break + default: + break + } + //let flags = readonly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE + let res = sqlite3_open(location.description, &self.handle) if res != SQLITE_OK { preconditionFailure("sqlite3_open_v2: \(res)") return nil diff --git a/Postbox/MappedFile.swift b/Postbox/MappedFile.swift index d0486c685f..23fb19dfcf 100644 --- a/Postbox/MappedFile.swift +++ b/Postbox/MappedFile.swift @@ -37,11 +37,11 @@ public final class MappedFile { msync(self.memory, self.currentSize, MS_ASYNC) } - public func write(at range: Range, from data: UnsafePointer) { + public func write(at range: Range, from data: UnsafeRawPointer) { memcpy(self.memory.advanced(by: range.lowerBound), data, range.count) } - public func read(at range: Range, to data: UnsafeMutablePointer) { + public func read(at range: Range, to data: UnsafeMutableRawPointer) { memcpy(data, self.memory.advanced(by: range.lowerBound), range.count) } diff --git a/Postbox/MediaBox.swift b/Postbox/MediaBox.swift index b66a83e838..13abd5cd0b 100644 --- a/Postbox/MediaBox.swift +++ b/Postbox/MediaBox.swift @@ -576,8 +576,9 @@ public final class MediaBox { return fetch(resource, currentSize ..< Int.max, tag) }) |> afterDisposed { dataQueue.async { - if let fd = fd { - close(fd) + if let thisFd = fd { + close(thisFd) + fd = nil } } }).start(next: { resultOption in @@ -750,8 +751,9 @@ public final class MediaBox { } } case let .moveLocalFile(tempPath): - if let fd = fd { - close(fd) + if let thisFd = thisFd { + close(thisFd) + fd = nil } unlink(paths.partial) do { diff --git a/Postbox/PeerMergedOperationLogView.swift b/Postbox/PeerMergedOperationLogView.swift index e07117b152..c403ced292 100644 --- a/Postbox/PeerMergedOperationLogView.swift +++ b/Postbox/PeerMergedOperationLogView.swift @@ -69,7 +69,7 @@ final class MutablePeerMergedOperationLogView { } for i in 0 ..< self.entries.count { if i != 0 { - assert(self.entries[i].mergedIndex == self.entries[i - 1].mergedIndex + 1) + assert(self.entries[i].mergedIndex >= self.entries[i - 1].mergedIndex + 1) } } if !self.entries.isEmpty { @@ -80,7 +80,7 @@ final class MutablePeerMergedOperationLogView { } else { assert(self.tailIndex != nil) if let tailIndex = self.tailIndex { - assert(self.entries.last!.mergedIndex == tailIndex) + assert(self.entries.last!.mergedIndex <= tailIndex) } } } diff --git a/Postbox/SqliteValueBox.swift b/Postbox/SqliteValueBox.swift index 3680431478..a9b2ab6097 100644 --- a/Postbox/SqliteValueBox.swift +++ b/Postbox/SqliteValueBox.swift @@ -141,12 +141,19 @@ final class SqliteValueBox: ValueBox { //database = Database(path)! } - database.execute("PRAGMA cache_size=-2097152") - database.execute("PRAGMA synchronous=NORMAL") - database.execute("PRAGMA journal_mode=WAL") - database.execute("PRAGMA temp_store=MEMORY") - database.execute("PRAGMA wal_autocheckpoint=500") - database.execute("PRAGMA journal_size_limit=1536") + sqlite3_busy_timeout(database.handle, 10000000) + + var resultCode: Bool + + //database.execute("PRAGMA cache_size=-2097152") + resultCode = database.execute("PRAGMA journal_mode=WAL") + assert(resultCode) + resultCode = database.execute("PRAGMA synchronous=NORMAL") + assert(resultCode) + //database.execute("PRAGMA temp_store=MEMORY") + resultCode = database.execute("PRAGMA wal_autocheckpoint=500") + assert(resultCode) + //database.execute("PRAGMA journal_size_limit=1536") /*var statement: OpaquePointer? = nil sqlite3_prepare_v2(database.handle, "PRAGMA integrity_check", -1, &statement, nil) @@ -161,13 +168,14 @@ final class SqliteValueBox: ValueBox { } preparedStatement.destroy()*/ - sqlite3_busy_timeout(database.handle, 10000000) - let result = self.getUserVersion(database) if result != 2 { - database.execute("PRAGMA user_version=2") - database.execute("DROP TABLE IF EXISTS __meta_tables") - database.execute("CREATE TABLE __meta_tables (name INTEGER, keyType INTEGER)") + resultCode = database.execute("PRAGMA user_version=2") + assert(resultCode) + resultCode = database.execute("DROP TABLE IF EXISTS __meta_tables") + assert(resultCode) + resultCode = database.execute("CREATE TABLE __meta_tables (name INTEGER, keyType INTEGER)") + assert(resultCode) } for table in self.listTables(database) { @@ -202,20 +210,23 @@ final class SqliteValueBox: ValueBox { public func begin() { assert(self.queue.isCurrent()) - self.database.execute("BEGIN IMMEDIATE") + let resultCode = self.database.execute("BEGIN IMMEDIATE") + assert(resultCode) } public func commit() { assert(self.queue.isCurrent()) let startTime = CFAbsoluteTimeGetCurrent() - self.database.execute("COMMIT") + let resultCode = self.database.execute("COMMIT") + assert(resultCode) self.commitTime += CFAbsoluteTimeGetCurrent() - startTime } private func getUserVersion(_ database: Database) -> Int64 { assert(self.queue.isCurrent()) var statement: OpaquePointer? = nil - sqlite3_prepare_v2(database.handle, "PRAGMA user_version", -1, &statement, nil) + let status = sqlite3_prepare_v2(database.handle, "PRAGMA user_version", -1, &statement, nil) + assert(status == SQLITE_OK) let preparedStatement = SqlitePreparedStatement(statement: statement) let _ = preparedStatement.step() let value = preparedStatement.int64At(0) @@ -226,7 +237,8 @@ final class SqliteValueBox: ValueBox { private func listTables(_ database: Database) -> [ValueBoxTable] { assert(self.queue.isCurrent()) var statement: OpaquePointer? = nil - sqlite3_prepare_v2(database.handle, "SELECT name, keyType FROM __meta_tables", -1, &statement, nil) + let status = sqlite3_prepare_v2(database.handle, "SELECT name, keyType FROM __meta_tables", -1, &statement, nil) + assert(status == SQLITE_OK) let preparedStatement = SqlitePreparedStatement(statement: statement) var tables: [ValueBoxTable] = [] while preparedStatement.step() { @@ -244,13 +256,18 @@ final class SqliteValueBox: ValueBox { } else { switch table.keyType { case .binary: - self.database.execute("CREATE TABLE t\(table.id) (key BLOB, value BLOB)") - self.database.execute("CREATE INDEX t\(table.id)_key ON t\(table.id) (key)") + var resultCode: Bool + resultCode = self.database.execute("CREATE TABLE t\(table.id) (key BLOB, value BLOB)") + assert(resultCode) + resultCode = self.database.execute("CREATE INDEX t\(table.id)_key ON t\(table.id) (key)") + assert(resultCode) case .int64: - self.database.execute("CREATE TABLE t\(table.id) (key INTEGER PRIMARY KEY, value BLOB)") + let resultCode = self.database.execute("CREATE TABLE t\(table.id) (key INTEGER PRIMARY KEY, value BLOB)") + assert(resultCode) } self.tables[table.id] = table - self.database.execute("INSERT INTO __meta_tables(name, keyType) VALUES (\(table.id), \(table.keyType.rawValue))") + let resultCode = self.database.execute("INSERT INTO __meta_tables(name, keyType) VALUES (\(table.id), \(table.keyType.rawValue))") + assert(resultCode) } } @@ -264,7 +281,8 @@ final class SqliteValueBox: ValueBox { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT value FROM t\(table.id) WHERE key=?", -1, &statement, nil) + let status = sqlite3_prepare_v2(self.database.handle, "SELECT value FROM t\(table.id) WHERE key=?", -1, &statement, nil) + assert(status == SQLITE_OK) let preparedStatement = SqlitePreparedStatement(statement: statement) self.getStatements[table.id] = preparedStatement resultStatement = preparedStatement @@ -293,7 +311,8 @@ final class SqliteValueBox: ValueBox { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key ASC LIMIT ?", -1, &statement, nil) + let status = sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key ASC LIMIT ?", -1, &statement, nil) + assert(status == SQLITE_OK) let preparedStatement = SqlitePreparedStatement(statement: statement) self.rangeKeyAscStatementsLimit[table.id] = preparedStatement resultStatement = preparedStatement @@ -325,7 +344,8 @@ final class SqliteValueBox: ValueBox { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key ASC", -1, &statement, nil) + let status = sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key ASC", -1, &statement, nil) + assert(status == SQLITE_OK) let preparedStatement = SqlitePreparedStatement(statement: statement) self.rangeKeyAscStatementsNoLimit[table.id] = preparedStatement resultStatement = preparedStatement @@ -355,7 +375,8 @@ final class SqliteValueBox: ValueBox { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key DESC LIMIT ?", -1, &statement, nil) + let status = sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key DESC LIMIT ?", -1, &statement, nil) + assert(status == SQLITE_OK) let preparedStatement = SqlitePreparedStatement(statement: statement) self.rangeKeyDescStatementsLimit[table.id] = preparedStatement resultStatement = preparedStatement @@ -386,7 +407,8 @@ final class SqliteValueBox: ValueBox { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key DESC", -1, &statement, nil) + let status = sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key DESC", -1, &statement, nil) + assert(status == SQLITE_OK) let preparedStatement = SqlitePreparedStatement(statement: statement) self.rangeKeyDescStatementsNoLimit[table.id] = preparedStatement resultStatement = preparedStatement @@ -417,7 +439,8 @@ final class SqliteValueBox: ValueBox { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key ASC LIMIT ?", -1, &statement, nil) + let status = sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key ASC LIMIT ?", -1, &statement, nil) + assert(status == SQLITE_OK) let preparedStatement = SqlitePreparedStatement(statement: statement) self.rangeValueAscStatementsLimit[table.id] = preparedStatement resultStatement = preparedStatement @@ -448,7 +471,8 @@ final class SqliteValueBox: ValueBox { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key ASC", -1, &statement, nil) + let status = sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key ASC", -1, &statement, nil) + assert(status == SQLITE_OK) let preparedStatement = SqlitePreparedStatement(statement: statement) self.rangeValueAscStatementsNoLimit[table.id] = preparedStatement resultStatement = preparedStatement @@ -479,7 +503,8 @@ final class SqliteValueBox: ValueBox { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key DESC LIMIT ?", -1, &statement, nil) + let status = sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key DESC LIMIT ?", -1, &statement, nil) + assert(status == SQLITE_OK) let preparedStatement = SqlitePreparedStatement(statement: statement) self.rangeValueDescStatementsLimit[table.id] = preparedStatement resultStatement = preparedStatement @@ -511,7 +536,8 @@ final class SqliteValueBox: ValueBox { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key DESC", -1, &statement, nil) + let status = sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table.id) WHERE key > ? AND key < ? ORDER BY key DESC", -1, &statement, nil) + assert(status == SQLITE_OK) let preparedStatement = SqlitePreparedStatement(statement: statement) self.rangeValueDescStatementsNoLimit[table.id] = preparedStatement resultStatement = preparedStatement @@ -540,7 +566,8 @@ final class SqliteValueBox: ValueBox { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table.id) ORDER BY key ASC", -1, &statement, nil) + let status = sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table.id) ORDER BY key ASC", -1, &statement, nil) + assert(status == SQLITE_OK) let preparedStatement = SqlitePreparedStatement(statement: statement) self.scanStatements[table.id] = preparedStatement resultStatement = preparedStatement @@ -561,7 +588,8 @@ final class SqliteValueBox: ValueBox { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "SELECT rowid FROM t\(table.id) WHERE key=?", -1, &statement, nil) + let status = sqlite3_prepare_v2(self.database.handle, "SELECT rowid FROM t\(table.id) WHERE key=?", -1, &statement, nil) + assert(status == SQLITE_OK) let preparedStatement = SqlitePreparedStatement(statement: statement) self.existsStatements[table.id] = preparedStatement resultStatement = preparedStatement @@ -589,7 +617,8 @@ final class SqliteValueBox: ValueBox { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "UPDATE t\(table.id) SET value=? WHERE key=?", -1, &statement, nil) + let status = sqlite3_prepare_v2(self.database.handle, "UPDATE t\(table.id) SET value=? WHERE key=?", -1, &statement, nil) + assert(status == SQLITE_OK) let preparedStatement = SqlitePreparedStatement(statement: statement) self.updateStatements[table.id] = preparedStatement resultStatement = preparedStatement @@ -618,7 +647,8 @@ final class SqliteValueBox: ValueBox { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "INSERT INTO t\(table.id) (key, value) VALUES(?, ?)", -1, &statement, nil) + let status = sqlite3_prepare_v2(self.database.handle, "INSERT INTO t\(table.id) (key, value) VALUES(?, ?)", -1, &statement, nil) + assert(status == SQLITE_OK) let preparedStatement = SqlitePreparedStatement(statement: statement) self.insertStatements[table.id] = preparedStatement resultStatement = preparedStatement @@ -651,7 +681,8 @@ final class SqliteValueBox: ValueBox { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "INSERT OR REPLACE INTO t\(table.id) (key, value) VALUES(?, ?)", -1, &statement, nil) + let status = sqlite3_prepare_v2(self.database.handle, "INSERT OR REPLACE INTO t\(table.id) (key, value) VALUES(?, ?)", -1, &statement, nil) + assert(status == SQLITE_OK) let preparedStatement = SqlitePreparedStatement(statement: statement) self.insertOrReplaceStatements[table.id] = preparedStatement resultStatement = preparedStatement @@ -684,7 +715,8 @@ final class SqliteValueBox: ValueBox { resultStatement = statement } else { var statement: OpaquePointer? = nil - sqlite3_prepare_v2(self.database.handle, "DELETE FROM t\(table.id) WHERE key=?", -1, &statement, nil) + let status = sqlite3_prepare_v2(self.database.handle, "DELETE FROM t\(table.id) WHERE key=?", -1, &statement, nil) + assert(status == SQLITE_OK) let preparedStatement = SqlitePreparedStatement(statement: statement) self.deleteStatements[table.id] = preparedStatement resultStatement = preparedStatement