Permalink
Browse files

Make ODB throw errors — for undefined objects, etc. These errors will…

… get mapped to script errors in Rainier.
  • Loading branch information...
brentsimmons committed Aug 28, 2018
1 parent 23d0b82 commit c3c9181356135e9999e6e2bd74656d21015e1e7c
@@ -23,6 +23,10 @@
8400AC0C1E0CFC3100AA7C57 /* FMResultSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 84DDF1951C94FC45005E6CF5 /* FMResultSet.m */; };
8400AC0F1E0CFC5600AA7C57 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8400AC0E1E0CFC5600AA7C57 /* libsqlite3.tbd */; };
8400AC101E0CFD6B00AA7C57 /* RSDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = 84F22C581B52E0D9000060CE /* RSDatabase.h */; settings = {ATTRIBUTES = (Public, ); }; };
84297FEB2133AE49004F3988 /* ODBTableValueLookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84297FEA2133AE49004F3988 /* ODBTableValueLookup.swift */; };
84297FEC2133AE49004F3988 /* ODBTableValueLookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84297FEA2133AE49004F3988 /* ODBTableValueLookup.swift */; };
84297FEE2133AEE5004F3988 /* ODBError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84297FED2133AEE5004F3988 /* ODBError.swift */; };
84297FEF2133AEE5004F3988 /* ODBError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84297FED2133AEE5004F3988 /* ODBError.swift */; };
84314F4A1F68ECC600F710B2 /* DatabaseObjectCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84314F491F68ECC600F710B2 /* DatabaseObjectCache.swift */; };
84419AD61B5ABD6D00C26BB2 /* FMDatabase+RSExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = 84419AD41B5ABD6D00C26BB2 /* FMDatabase+RSExtras.h */; settings = {ATTRIBUTES = (Public, ); }; };
84419AD71B5ABD6D00C26BB2 /* FMDatabase+RSExtras.m in Sources */ = {isa = PBXBuildFile; fileRef = 84419AD51B5ABD6D00C26BB2 /* FMDatabase+RSExtras.m */; };
@@ -86,6 +90,8 @@
8400ABF71E0CFBD800AA7C57 /* RSDatabase.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RSDatabase.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8400ABFA1E0CFBD800AA7C57 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
8400AC0E1E0CFC5600AA7C57 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.2.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
84297FEA2133AE49004F3988 /* ODBTableValueLookup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ODBTableValueLookup.swift; sourceTree = "<group>"; };
84297FED2133AEE5004F3988 /* ODBError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ODBError.swift; sourceTree = "<group>"; };
84314F491F68ECC600F710B2 /* DatabaseObjectCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DatabaseObjectCache.swift; path = RSDatabase/DatabaseObjectCache.swift; sourceTree = "<group>"; };
84419AD41B5ABD6D00C26BB2 /* FMDatabase+RSExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "FMDatabase+RSExtras.h"; path = "RSDatabase/FMDatabase+RSExtras.h"; sourceTree = "<group>"; };
84419AD51B5ABD6D00C26BB2 /* FMDatabase+RSExtras.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "FMDatabase+RSExtras.m"; path = "RSDatabase/FMDatabase+RSExtras.m"; sourceTree = "<group>"; };
@@ -255,9 +261,11 @@
children = (
84BB215620F13C5700AF2687 /* README.markdown */,
84F4A2E0208B0719008BA1F8 /* ODB.swift */,
84297FED2133AEE5004F3988 /* ODBError.swift */,
84F4A2FF208BE763008BA1F8 /* ODBPath.swift */,
844725EF20903E720027551E /* ODBObject.swift */,
84F4A302208BE7A5008BA1F8 /* ODBTable.swift */,
84297FEA2133AE49004F3988 /* ODBTableValueLookup.swift */,
84CED26C2090325C0073B251 /* ODBValue.swift */,
84F4A305208BE7AF008BA1F8 /* ODBValueObject.swift */,
84F4A2F6208B0C8A008BA1F8 /* ODBTablesTable.swift */,
@@ -458,8 +466,10 @@
84F4A2FB208B0D56008BA1F8 /* ODBValuesTable.swift in Sources */,
84ABC1D21F364B07000DCC55 /* DatabaseLookupTable.swift in Sources */,
84F4A2F8208B0C8A008BA1F8 /* ODBTablesTable.swift in Sources */,
84297FEC2133AE49004F3988 /* ODBTableValueLookup.swift in Sources */,
8400AC0C1E0CFC3100AA7C57 /* FMResultSet.m in Sources */,
8400AC021E0CFC0700AA7C57 /* FMDatabase+RSExtras.m in Sources */,
84297FEF2133AEE5004F3988 /* ODBError.swift in Sources */,
84F4A307208BE7AF008BA1F8 /* ODBValueObject.swift in Sources */,
844725F120903E720027551E /* ODBObject.swift in Sources */,
8400AC081E0CFC2000AA7C57 /* FMDatabase.m in Sources */,
@@ -482,7 +492,9 @@
84419ADB1B5ABD7400C26BB2 /* NSString+RSDatabase.m in Sources */,
84ABC1D11F364B07000DCC55 /* DatabaseLookupTable.swift in Sources */,
84DDF1971C94FC45005E6CF5 /* FMDatabase.m in Sources */,
84297FEE2133AEE5004F3988 /* ODBError.swift in Sources */,
84F4A2F7208B0C8A008BA1F8 /* ODBTablesTable.swift in Sources */,
84297FEB2133AE49004F3988 /* ODBTableValueLookup.swift in Sources */,
84F4A303208BE7A5008BA1F8 /* ODBTable.swift in Sources */,
84CED26D2090325C0073B251 /* ODBValue.swift in Sources */,
84DDF1A01C94FC45005E6CF5 /* FMResultSet.m in Sources */,
View
@@ -10,115 +10,128 @@ import Foundation
// This is not thread-safe. Neither are the other ODB* objects and structs.
// It’s up to the caller to implement thread safety.
// Recommended use: in your code, create references to ODB and ODBPath,
// Recommended use: in your code, create references to ODB, ODBPath, and ODBValue,
// and use ODBObject, ODBTable, and ODBValueObject the least amount possible. Do not keep references to them.
public final class ODB: Hashable {
public let filepath: String
public lazy var rootTable: ODBTable = {
ODBTable(uniqueID: -1, name: ODBPath.rootTableName, parentTable: nil, isRootTable: true, odb: self)
}()
/// It’s an error to use the ODB once closed. This exists because somebody have kept a reference to the ODB.
/// Call odb.close() when finished with it.
public var isClosed = false
static let rootTableID = -1
private let queue: RSDatabaseQueue
private lazy var odbValuesTable: ODBValuesTable = {
return ODBValuesTable(odb: self)
}()
private lazy var odbTablesTable: ODBTablesTable = {
return ODBTablesTable(odb: self)
public lazy var rootTable: ODBTable = {
ODBTable(uniqueID: ODB.rootTableID, name: ODBPath.rootTableName, parentTable: nil, isRootTable: true, odb: self)
}()
private static let tableCreationStatements = """
CREATE TABLE if not EXISTS odb_tables (id INTEGER PRIMARY KEY AUTOINCREMENT, parent_id INTEGER NOT NULL, name TEXT NOT NULL);
CREATE TABLE if not EXISTS odb_values (id INTEGER PRIMARY KEY AUTOINCREMENT, odb_table_id INTEGER NOT NULL, name TEXT NOT NULL, primitive_type INTEGER NOT NULL, application_type TEXT, value BLOB);
CREATE INDEX if not EXISTS odb_tables_parent_id_index on odb_tables (parent_id);
CREATE INDEX if not EXISTS odb_values_odb_table_id_index on odb_values (odb_table_id);
CREATE TRIGGER if not EXISTS odb_tables_after_delete_trigger_delete_subtables after delete on odb_tables begin delete from odb_tables where parent_id = OLD.id; end;
CREATE TRIGGER if not EXISTS odb_tables_after_delete_trigger_delete_child_values after delete on odb_tables begin delete from odb_values where odb_table_id = OLD.id; end;
"""
private let queue: RSDatabaseQueue
private var odbTablesTable: ODBTablesTable? = ODBTablesTable()
private var odbValuesTable: ODBValuesTable? = ODBValuesTable()
public init(filepath: String) {
self.filepath = filepath
let queue = RSDatabaseQueue(filepath: filepath, excludeFromBackup: false)
queue.createTables(usingStatements: ODB.tableCreationStatements)
self.queue = queue
}
/// Call when finished, to make sure no stray references can do undefined things.
/// It’s not necessary to call this on app termination.
public func close() {
isClosed = true
odbValuesTable = nil
odbTablesTable = nil
}
/// Make sure it’s okay to use the odb — check that it hasn’t been closed.
public func preflightCall() throws {
if isClosed {
throw ODBError.odbClosed(filePath: filepath)
}
}
// MARK: - Hashable
public func hash(into hasher: inout Hasher) {
hasher.combine(filepath)
}
// MARK: - Equatable
public static func ==(lhs: ODB, rhs: ODB) -> Bool {
return lhs.filepath == rhs.filepath
}
}
extension ODB {
func deleteObject(_ object: ODBObject) {
func deleteObject(_ object: ODBObject) throws {
try preflightCall()
if let valueObject = object as? ODBValueObject {
let uniqueID = valueObject.uniqueID
queue.update { (database) in
self.odbValuesTable.deleteObject(uniqueID: uniqueID, database: database)
self.odbValuesTable!.deleteObject(uniqueID: uniqueID, database: database)
}
}
else if let tableObject = object as? ODBTable {
let uniqueID = tableObject.uniqueID
queue.update { (database) in
self.odbTablesTable.deleteTable(uniqueID: uniqueID, database: database)
self.odbTablesTable!.deleteTable(uniqueID: uniqueID, database: database)
}
}
else {
preconditionFailure("deleteObject: object neither ODBValueObject or ODBTable")
}
}
func deleteChildren(of table: ODBTable) {
func deleteChildren(of table: ODBTable) throws {
try preflightCall()
let parentUniqueID = table.uniqueID
queue.update { (database) in
self.odbTablesTable.deleteChildTables(parentUniqueID: parentUniqueID, database: database)
self.odbValuesTable.deleteChildObjects(parentUniqueID: parentUniqueID, database: database)
self.odbTablesTable!.deleteChildTables(parentUniqueID: parentUniqueID, database: database)
self.odbValuesTable!.deleteChildObjects(parentUniqueID: parentUniqueID, database: database)
}
}
func insertTable(name: String, parent: ODBTable) -> ODBTable? {
func insertTable(name: String, parent: ODBTable) throws -> ODBTable {
try preflightCall()
var table: ODBTable? = nil
queue.fetchSync { (database) in
table = self.odbTablesTable.insertTable(name: name, parentTable: parent, database: database)
table = self.odbTablesTable!.insertTable(name: name, parentTable: parent, odb: self, database: database)
}
return table
return table!
}
func insertValueObject(name: String, value: ODBValue, parent: ODBTable) -> ODBValueObject? {
func insertValueObject(name: String, value: ODBValue, parent: ODBTable) throws -> ODBValueObject {
try preflightCall()
var valueObject: ODBValueObject? = nil
queue.updateSync { (database) in
valueObject = self.odbValuesTable.insertValueObject(name: name, value: value, parentTable: parent, database: database)
valueObject = self.odbValuesTable!.insertValueObject(name: name, value: value, parentTable: parent, database: database)
}
return valueObject
return valueObject!
}
func fetchChildren(of table: ODBTable) -> ODBDictionary {
func fetchChildren(of table: ODBTable) throws -> ODBDictionary {
try preflightCall()
var children = ODBDictionary()
queue.fetchSync { (database) in
let tables = self.odbTablesTable.fetchSubtables(of: table, database: database)
let valueObjects = self.odbValuesTable.fetchValueObjects(of: table, database: database)
let tables = self.odbTablesTable!.fetchSubtables(of: table, database: database, odb: self)
let valueObjects = self.odbValuesTable!.fetchValueObjects(of: table, database: database)
// Keys are lower-cased, since we case-insensitive lookups.
@@ -137,6 +150,21 @@ extension ODB {
}
}
private extension ODB {
static let tableCreationStatements = """
CREATE TABLE if not EXISTS odb_tables (id INTEGER PRIMARY KEY AUTOINCREMENT, parent_id INTEGER NOT NULL, name TEXT NOT NULL);
CREATE TABLE if not EXISTS odb_values (id INTEGER PRIMARY KEY AUTOINCREMENT, odb_table_id INTEGER NOT NULL, name TEXT NOT NULL, primitive_type INTEGER NOT NULL, application_type TEXT, value BLOB);
CREATE INDEX if not EXISTS odb_tables_parent_id_index on odb_tables (parent_id);
CREATE INDEX if not EXISTS odb_values_odb_table_id_index on odb_values (odb_table_id);
CREATE TRIGGER if not EXISTS odb_tables_after_delete_trigger_delete_subtables after delete on odb_tables begin delete from odb_tables where parent_id = OLD.id; end;
CREATE TRIGGER if not EXISTS odb_tables_after_delete_trigger_delete_child_values after delete on odb_tables begin delete from odb_values where odb_table_id = OLD.id; end;
"""
}
extension String {
private static let lowercaseLocale = Locale(identifier: "en")
@@ -0,0 +1,18 @@
//
// ODBError.swift
// RSDatabase
//
// Created by Brent Simmons on 8/26/18.
// Copyright © 2018 Ranchero Software, LLC. All rights reserved.
//
import Foundation
public enum ODBError: Error {
case undefined(path: ODBPath)
case notATable(path: ODBPath)
case notAValue(path: ODBPath)
case invalidDataType(rawValue: Any)
case odbClosed(filePath: String)
case illegalOperationOnRootTable(path: ODBPath)
}
@@ -13,27 +13,6 @@ public typealias ODBDictionary = [String: ODBObject]
// ODBTable and ODBValueObject conform to ODBObject.
public protocol ODBObject {
var name: String { get }
var isRootTable: Bool { get }
var parentTable: ODBTable? { get }
var path: ODBPath? { get }
var odb: ODB { get }
func delete()
}
public extension ODBObject {
func delete() {
guard !isRootTable else {
preconditionFailure("Can’t delete root table.")
}
guard let parentTable = parentTable else {
preconditionFailure("Expected parent table for object, found nil.")
}
parentTable.deleteChild(self)
}
}
Oops, something went wrong.

0 comments on commit c3c9181

Please sign in to comment.