Skip to content

Commit b9247db

Browse files
committed
Convert fatalErrors to Swift error handling
Most fatalErrors have been replaced with proper Swift error handling. Aside from being more "correct" Swift code this also allows errors during binding to be thrown from external code and handled in places like custom function invocation.
1 parent b7a0eb1 commit b9247db

13 files changed

+226
-153
lines changed

SQLite.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
03A65E941C6BB3030062603F /* ValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B331C3F142E00AE3E12 /* ValueTests.swift */; };
4747
03A65E951C6BB3030062603F /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B161C3F127200AE3E12 /* TestHelpers.swift */; };
4848
03A65E971C6BB3210062603F /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A65E961C6BB3210062603F /* libsqlite3.tbd */; };
49+
AA58505F1CC40BA70034C46D /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA58505E1CC40BA70034C46D /* Errors.swift */; };
4950
AA780B3D1CC201A700E0E95E /* ConnectionPool.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA780B3B1CC201A700E0E95E /* ConnectionPool.swift */; };
5051
AA780B3E1CC201A700E0E95E /* Dispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA780B3C1CC201A700E0E95E /* Dispatcher.swift */; };
5152
AA780B411CC202C800E0E95E /* ConnectionPoolTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA780B3F1CC202B000E0E95E /* ConnectionPoolTests.swift */; };
@@ -174,6 +175,7 @@
174175
39548A6F1CA63C740003E3B5 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
175176
A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
176177
AA780B3B1CC201A700E0E95E /* ConnectionPool.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ConnectionPool.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
178+
AA58505E1CC40BA70034C46D /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
177179
AA780B3C1CC201A700E0E95E /* Dispatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Dispatcher.swift; sourceTree = "<group>"; };
178180
AA780B3F1CC202B000E0E95E /* ConnectionPoolTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionPoolTests.swift; sourceTree = "<group>"; };
179181
EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -435,6 +437,7 @@
435437
EE247AF11C3F06E900AE3E12 /* SQLite-Bridging.m */,
436438
EE247AF21C3F06E900AE3E12 /* Statement.swift */,
437439
EE247AF31C3F06E900AE3E12 /* Value.swift */,
440+
AA58505E1CC40BA70034C46D /* Errors.swift */,
438441
);
439442
path = Core;
440443
sourceTree = "<group>";
@@ -855,6 +858,7 @@
855858
EE247B091C3F06E900AE3E12 /* FTS4.swift in Sources */,
856859
EE247B081C3F06E900AE3E12 /* Value.swift in Sources */,
857860
EE247B121C3F06E900AE3E12 /* Operators.swift in Sources */,
861+
AA58505F1CC40BA70034C46D /* Errors.swift in Sources */,
858862
EE247B141C3F06E900AE3E12 /* Schema.swift in Sources */,
859863
AA780B3E1CC201A700E0E95E /* Dispatcher.swift in Sources */,
860864
EE247B131C3F06E900AE3E12 /* Query.swift in Sources */,

SQLite/Core/Connection.swift

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -701,48 +701,59 @@ public final class DirectConnection : Connection, Equatable {
701701
/// - block: A block of code to run when the function is called. The block
702702
/// is called with an array of raw SQL values mapped to the function’s
703703
/// parameters and should return a raw SQL value (or nil).
704-
public func createFunction(function: String, argumentCount: UInt? = nil, deterministic: Bool = false, _ block: (args: [Binding?]) -> Binding?) {
704+
public func createFunction(function: String, argumentCount: UInt? = nil, deterministic: Bool = false, _ block: (args: [Binding?]) throws -> Binding?) throws {
705705
let argc = argumentCount.map { Int($0) } ?? -1
706706
let box: Function = { context, argc, argv in
707-
let arguments: [Binding?] = (0..<Int(argc)).map { idx in
708-
let value = argv[idx]
709-
switch sqlite3_value_type(value) {
710-
case SQLITE_BLOB:
711-
return Blob(bytes: sqlite3_value_blob(value), length: Int(sqlite3_value_bytes(value)))
712-
case SQLITE_FLOAT:
713-
return sqlite3_value_double(value)
714-
case SQLITE_INTEGER:
715-
return sqlite3_value_int64(value)
716-
case SQLITE_NULL:
717-
return nil
718-
case SQLITE_TEXT:
719-
return String.fromCString(UnsafePointer(sqlite3_value_text(value)))!
720-
case let type:
721-
fatalError("unsupported value type: \(type)")
707+
do {
708+
let arguments: [Binding?] = try (0..<Int(argc)).map { idx in
709+
let value = argv[idx]
710+
switch sqlite3_value_type(value) {
711+
case SQLITE_BLOB:
712+
return Blob(bytes: sqlite3_value_blob(value), length: Int(sqlite3_value_bytes(value)))
713+
case SQLITE_FLOAT:
714+
return sqlite3_value_double(value)
715+
case SQLITE_INTEGER:
716+
return sqlite3_value_int64(value)
717+
case SQLITE_NULL:
718+
return nil
719+
case SQLITE_TEXT:
720+
return String.fromCString(UnsafePointer(sqlite3_value_text(value)))!
721+
case let type:
722+
throw FunctionError.UnsupportedArgumentType(type: type)
723+
}
724+
}
725+
let result = try block(args: arguments)
726+
if let result = result as? Blob {
727+
sqlite3_result_blob(context, result.bytes, Int32(result.bytes.count), nil)
728+
} else if let result = result as? Double {
729+
sqlite3_result_double(context, result)
730+
} else if let result = result as? Int64 {
731+
sqlite3_result_int64(context, result)
732+
} else if let result = result as? String {
733+
sqlite3_result_text(context, result, Int32(result.characters.count), SQLITE_TRANSIENT)
734+
} else if let result = result {
735+
throw FunctionError.UnsupportedResultType(value: result)
736+
} else {
737+
sqlite3_result_null(context)
722738
}
723739
}
724-
let result = block(args: arguments)
725-
if let result = result as? Blob {
726-
sqlite3_result_blob(context, result.bytes, Int32(result.bytes.count), nil)
727-
} else if let result = result as? Double {
728-
sqlite3_result_double(context, result)
729-
} else if let result = result as? Int64 {
730-
sqlite3_result_int64(context, result)
731-
} else if let result = result as? String {
732-
sqlite3_result_text(context, result, Int32(result.characters.count), SQLITE_TRANSIENT)
733-
} else if result == nil {
734-
sqlite3_result_null(context)
735-
} else {
736-
fatalError("unsupported result type: \(result)")
740+
catch let error as CustomStringConvertible {
741+
sqlite3_result_error(context, error.description, 0)
742+
}
743+
catch let error as NSError {
744+
sqlite3_result_error(context, error.localizedDescription, 0)
745+
}
746+
catch {
747+
sqlite3_result_error(context, "Unknown invocation error", 0)
737748
}
738749
}
739750
var flags = SQLITE_UTF8
740751
if deterministic {
741752
flags |= SQLITE_DETERMINISTIC
742753
}
743-
sqlite3_create_function_v2(handle, function, Int32(argc), flags, unsafeBitCast(box, UnsafeMutablePointer<Void>.self), { context, argc, value in
754+
try check(sqlite3_create_function_v2(handle, function, Int32(argc), flags, unsafeBitCast(box, UnsafeMutablePointer<Void>.self), { context, argc, value in
744755
unsafeBitCast(sqlite3_user_data(context), Function.self)(context, argc, value)
745-
}, nil, nil, nil)
756+
}, nil, nil, nil))
746757
if functions[function] == nil { self.functions[function] = [:] }
747758
functions[function]?[argc] = box
748759
}

SQLite/Core/Errors.swift

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//
2+
// Errors.swift
3+
// SQLite
4+
//
5+
// Created by Kevin Wooten on 4/17/16.
6+
//
7+
//
8+
9+
import Foundation
10+
11+
12+
public enum FunctionError : ErrorType, CustomStringConvertible {
13+
case UnsupportedArgumentType(type: Int32)
14+
case UnsupportedResultType(value: Binding)
15+
16+
public var description : String {
17+
switch self {
18+
case UnsupportedArgumentType(let type):
19+
return "Unsupported argument type: \(type)"
20+
case UnsupportedResultType(let value):
21+
return "Unsupported result type for value: \(value)"
22+
}
23+
}
24+
}
25+
26+
public enum BindingError : ErrorType, CustomStringConvertible {
27+
case UnsupportedType(value: Binding)
28+
case ParameterNotFound(name: String)
29+
case IncorrectParameterCount(expected: Int, provided: Int)
30+
31+
public var description : String {
32+
switch self {
33+
case UnsupportedType(let value):
34+
return "Unsupported type for value: \(value)"
35+
case ParameterNotFound(let name):
36+
return "Parameter not found: \(name)"
37+
case let IncorrectParameterCount(expected, provided):
38+
return "Incorrect parameter count, \(provided) provided with \(expected) expected"
39+
}
40+
}
41+
}
42+
43+
public enum QueryError : ErrorType, CustomStringConvertible {
44+
case NoSuchTable(name: String)
45+
case NoSuchColumn(name: String)
46+
case AmbiguousColumn(name: String)
47+
48+
public var description : String {
49+
switch self {
50+
case NoSuchTable(let name):
51+
return "No such table: \(name)"
52+
case NoSuchColumn(let name):
53+
return "No such column: \(name)"
54+
case AmbiguousColumn(let name):
55+
return "Ambiguous column: \(name)"
56+
}
57+
}
58+
}

SQLite/Core/Statement.swift

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,22 +54,22 @@ public final class Statement {
5454
/// - Parameter values: A list of parameters to bind to the statement.
5555
///
5656
/// - Returns: The statement object (useful for chaining).
57-
public func bind(values: Binding?...) -> Statement {
58-
return bind(values)
57+
public func bind(values: Binding?...) throws -> Statement {
58+
return try bind(values)
5959
}
6060

6161
/// Binds a list of parameters to a statement.
6262
///
6363
/// - Parameter values: A list of parameters to bind to the statement.
6464
///
6565
/// - Returns: The statement object (useful for chaining).
66-
public func bind(values: [Binding?]) -> Statement {
66+
public func bind(values: [Binding?]) throws -> Statement {
6767
if values.isEmpty { return self }
6868
reset()
6969
guard values.count == Int(sqlite3_bind_parameter_count(handle)) else {
70-
fatalError("\(sqlite3_bind_parameter_count(handle)) values expected, \(values.count) passed")
70+
throw BindingError.IncorrectParameterCount(expected: Int(sqlite3_bind_parameter_count(handle)), provided: values.count)
7171
}
72-
for idx in 1...values.count { bind(values[idx - 1], atIndex: idx) }
72+
for idx in 1...values.count { try bind(values[idx - 1], atIndex: idx) }
7373
return self
7474
}
7575

@@ -79,19 +79,19 @@ public final class Statement {
7979
/// statement.
8080
///
8181
/// - Returns: The statement object (useful for chaining).
82-
public func bind(values: [String: Binding?]) -> Statement {
82+
public func bind(values: [String: Binding?]) throws -> Statement {
8383
reset()
8484
for (name, value) in values {
8585
let idx = sqlite3_bind_parameter_index(handle, name)
8686
guard idx > 0 else {
87-
fatalError("parameter not found: \(name)")
87+
throw BindingError.ParameterNotFound(name: name)
8888
}
89-
bind(value, atIndex: Int(idx))
89+
try bind(value, atIndex: Int(idx))
9090
}
9191
return self
9292
}
9393

94-
private func bind(value: Binding?, atIndex idx: Int) {
94+
private func bind(value: Binding?, atIndex idx: Int) throws {
9595
if value == nil {
9696
sqlite3_bind_null(handle, Int32(idx))
9797
} else if let value = value as? Blob {
@@ -103,11 +103,11 @@ public final class Statement {
103103
} else if let value = value as? String {
104104
sqlite3_bind_text(handle, Int32(idx), value, -1, SQLITE_TRANSIENT)
105105
} else if let value = value as? Int {
106-
self.bind(value.datatypeValue, atIndex: idx)
106+
try self.bind(try value.datatypeValue(), atIndex: idx)
107107
} else if let value = value as? Bool {
108-
self.bind(value.datatypeValue, atIndex: idx)
108+
try self.bind(try value.datatypeValue(), atIndex: idx)
109109
} else if let value = value {
110-
fatalError("tried to bind unexpected value \(value)")
110+
throw BindingError.UnsupportedType(value: value)
111111
}
112112
}
113113

@@ -148,9 +148,9 @@ public final class Statement {
148148
/// - Parameter bindings: A list of parameters to bind to the statement.
149149
///
150150
/// - Returns: The first value of the first row returned.
151-
@warn_unused_result public func scalar(bindings: Binding?...) -> Binding? {
151+
@warn_unused_result public func scalar(bindings: Binding?...) throws -> Binding? {
152152
guard bindings.isEmpty else {
153-
return scalar(bindings)
153+
return try scalar(bindings)
154154
}
155155

156156
reset(clearBindings: false)
@@ -161,17 +161,17 @@ public final class Statement {
161161
/// - Parameter bindings: A list of parameters to bind to the statement.
162162
///
163163
/// - Returns: The first value of the first row returned.
164-
@warn_unused_result public func scalar(bindings: [Binding?]) -> Binding? {
165-
return bind(bindings).scalar()
164+
@warn_unused_result public func scalar(bindings: [Binding?]) throws -> Binding? {
165+
return try bind(bindings).scalar()
166166
}
167167

168168

169169
/// - Parameter bindings: A dictionary of named parameters to bind to the
170170
/// statement.
171171
///
172172
/// - Returns: The first value of the first row returned.
173-
@warn_unused_result public func scalar(bindings: [String: Binding?]) -> Binding? {
174-
return bind(bindings).scalar()
173+
@warn_unused_result public func scalar(bindings: [String: Binding?]) throws -> Binding? {
174+
return try bind(bindings).scalar()
175175
}
176176

177177
public func step() throws -> Bool {

SQLite/Core/Value.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ public protocol Value : Expressible { // extensions cannot have inheritance clau
3939

4040
static var declaredDatatype: String { get }
4141

42-
static func fromDatatypeValue(datatypeValue: Datatype) -> ValueType
42+
static func fromDatatypeValue(datatypeValue: Datatype) throws -> ValueType
4343

44-
var datatypeValue: Datatype { get }
44+
func datatypeValue() throws -> Datatype
4545

4646
}
4747

@@ -53,7 +53,7 @@ extension Double : Number, Value {
5353
return datatypeValue
5454
}
5555

56-
public var datatypeValue: Double {
56+
public func datatypeValue() throws -> Double {
5757
return self
5858
}
5959

@@ -67,7 +67,7 @@ extension Int64 : Number, Value {
6767
return datatypeValue
6868
}
6969

70-
public var datatypeValue: Int64 {
70+
public func datatypeValue() throws -> Int64 {
7171
return self
7272
}
7373

@@ -81,7 +81,7 @@ extension String : Binding, Value {
8181
return datatypeValue
8282
}
8383

84-
public var datatypeValue: String {
84+
public func datatypeValue() throws -> String {
8585
return self
8686
}
8787

@@ -95,7 +95,7 @@ extension Blob : Binding, Value {
9595
return datatypeValue
9696
}
9797

98-
public var datatypeValue: Blob {
98+
public func datatypeValue() throws -> Blob {
9999
return self
100100
}
101101

@@ -111,7 +111,7 @@ extension Bool : Binding, Value {
111111
return datatypeValue != 0
112112
}
113113

114-
public var datatypeValue: Int64 {
114+
public func datatypeValue() throws -> Int64 {
115115
return self ? 1 : 0
116116
}
117117

@@ -125,7 +125,7 @@ extension Int : Number, Value {
125125
return Int(datatypeValue)
126126
}
127127

128-
public var datatypeValue: Int64 {
128+
public func datatypeValue() throws -> Int64 {
129129
return Int64(self)
130130
}
131131

SQLite/Foundation.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ extension NSData : Value {
3434
return NSData(bytes: dataValue.bytes, length: dataValue.bytes.count)
3535
}
3636

37-
public var datatypeValue: Blob {
37+
public func datatypeValue() throws -> Blob {
3838
return Blob(bytes: bytes, length: length)
3939
}
4040

@@ -50,7 +50,7 @@ extension NSDate : Value {
5050
return dateFormatter.dateFromString(stringValue)!
5151
}
5252

53-
public var datatypeValue: String {
53+
public func datatypeValue() throws -> String {
5454
return dateFormatter.stringFromDate(self)
5555
}
5656

@@ -90,17 +90,17 @@ extension QueryType {
9090
extension Row {
9191

9292
public subscript(column: Expression<NSData>) -> NSData {
93-
return get(column)
93+
return try! get(column)
9494
}
9595
public subscript(column: Expression<NSData?>) -> NSData? {
96-
return get(column)
96+
return try! get(column)
9797
}
9898

9999
public subscript(column: Expression<NSDate>) -> NSDate {
100-
return get(column)
100+
return try! get(column)
101101
}
102102
public subscript(column: Expression<NSDate?>) -> NSDate? {
103-
return get(column)
103+
return try! get(column)
104104
}
105105

106106
}

0 commit comments

Comments
 (0)