Skip to content

Commit

Permalink
Row.get should throw, not crash
Browse files Browse the repository at this point in the history
  • Loading branch information
jberkel committed Sep 16, 2017
1 parent b8d6a9c commit bd1fc80
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 9 deletions.
10 changes: 10 additions & 0 deletions SQLite.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,26 @@
19A171F12AB8B07F2FD7201A /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
19A17254FBA7894891F7297B /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; };
19A17408007B182F884E3A53 /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B93B48B5560E6E51791 /* Fixtures.swift */; };
19A17490543609FCED53CACC /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
19A174D78559CD30679BCCCB /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; };
19A1750CEE9B05267995CF3D /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
19A175DFF47B84757E547C62 /* fixtures in Resources */ = {isa = PBXBuildFile; fileRef = 19A17E2695737FAB5D6086E3 /* fixtures */; };
19A177CC33F2E6A24AF90B02 /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; };
19A178072B371489E6A1E839 /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
19A17835FD5886FDC5A3228F /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
19A1792C0520D4E83C2EB075 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
19A179A0C45377CB09BB358C /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; };
19A179CCF9671E345E5A9811 /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
19A179E76EA6207669B60C1B /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
19A17C4B951CB054EE48AB1C /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; };
19A17DC282E36C4F41AA440B /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
19A17E04C4C0956715C5676A /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
19A17EC0D68BA8C03288ADF7 /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
19A17F3E1F7ACA33BD43E138 /* fixtures in Resources */ = {isa = PBXBuildFile; fileRef = 19A17E2695737FAB5D6086E3 /* fixtures */; };
19A17F60B685636D1F83C2DD /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B93B48B5560E6E51791 /* Fixtures.swift */; };
19A17FB80B94E882050AA908 /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
19A17FDA323BAFDEC627E76F /* fixtures in Resources */ = {isa = PBXBuildFile; fileRef = 19A17E2695737FAB5D6086E3 /* fixtures */; };
19A17FF4A10B44D3937C8CAC /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
3D67B3E61DB2469200A4F4C6 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D67B3E51DB2469200A4F4C6 /* libsqlite3.tbd */; };
3D67B3E71DB246BA00A4F4C6 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
3D67B3E81DB246BA00A4F4C6 /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEF1C3F06E900AE3E12 /* Connection.swift */; };
Expand Down Expand Up @@ -196,6 +200,7 @@
03A65E5A1C6BB0F50062603F /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
03A65E631C6BB0F60062603F /* SQLiteTests tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SQLiteTests tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
03A65E961C6BB3210062603F /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
19A1710E73A46D5AC721CDA9 /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
19A1721B8984686B9963B45D /* FTS5Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS5Tests.swift; sourceTree = "<group>"; };
19A1730E4390C775C25677D1 /* FTS5.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS5.swift; sourceTree = "<group>"; };
19A17399EA9E61235D5D77BF /* CipherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CipherTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -408,6 +413,7 @@
EE247AF11C3F06E900AE3E12 /* SQLite-Bridging.m */,
EE247AF21C3F06E900AE3E12 /* Statement.swift */,
EE247AF31C3F06E900AE3E12 /* Value.swift */,
19A1710E73A46D5AC721CDA9 /* Errors.swift */,
);
path = Core;
sourceTree = "<group>";
Expand Down Expand Up @@ -787,6 +793,7 @@
03A65E7E1C6BB2FB0062603F /* AggregateFunctions.swift in Sources */,
19A17EC0D68BA8C03288ADF7 /* FTS5.swift in Sources */,
19A179E76EA6207669B60C1B /* Cipher.swift in Sources */,
19A17FF4A10B44D3937C8CAC /* Errors.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -840,6 +847,7 @@
3D67B3E71DB246BA00A4F4C6 /* Blob.swift in Sources */,
3D67B3E81DB246BA00A4F4C6 /* Connection.swift in Sources */,
19A179CCF9671E345E5A9811 /* Cipher.swift in Sources */,
19A17DC282E36C4F41AA440B /* Errors.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -867,6 +875,7 @@
EE247B0D1C3F06E900AE3E12 /* AggregateFunctions.swift in Sources */,
19A1717B10CC941ACB5533D6 /* FTS5.swift in Sources */,
19A171F12AB8B07F2FD7201A /* Cipher.swift in Sources */,
19A1792C0520D4E83C2EB075 /* Errors.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -920,6 +929,7 @@
EE247B6D1C3F3FEC00AE3E12 /* AggregateFunctions.swift in Sources */,
19A1750CEE9B05267995CF3D /* FTS5.swift in Sources */,
19A17835FD5886FDC5A3228F /* Cipher.swift in Sources */,
19A17490543609FCED53CACC /* Errors.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
18 changes: 18 additions & 0 deletions Sources/SQLite/Core/Errors.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Foundation

public enum QueryError: Error, CustomStringConvertible {
case noSuchTable(name: String)
case noSuchColumn(name: String, columns: [String])
case ambiguousColumn(name: String, similar: [String])

public var description: String {
switch self {
case .noSuchTable(let name):
return "No such table: \(name)"
case .noSuchColumn(let name, let columns):
return "No such column `\(name)` in columns \(columns)"
case .ambiguousColumn(let name, let similar):
return "Ambiguous column `\(name)` (please disambiguate: \(similar))"
}
}
}
19 changes: 10 additions & 9 deletions Sources/SQLite/Typed/Query.swift
Original file line number Diff line number Diff line change
Expand Up @@ -929,7 +929,7 @@ extension Connection {
continue column
}
}
fatalError("no such table: \(namespace)")
throw QueryError.noSuchTable(name: namespace)
}
for q in queries {
try expandGlob(query.clauses.join.count > 0)(q)
Expand Down Expand Up @@ -1041,37 +1041,38 @@ public struct Row {
/// - Parameter column: An expression representing a column selected in a Query.
///
/// - Returns: The value for the given column.
public func get<V: Value>(_ column: Expression<V>) -> V {
return get(Expression<V?>(column))!
public func get<V: Value>(_ column: Expression<V>) throws -> V {
return try get(Expression<V?>(column))!

This comment has been minimized.

Copy link
@nickmshelley

nickmshelley Sep 17, 2017

Collaborator

Won't this still crash if there's a type mismatch?

This comment has been minimized.

Copy link
@jberkel

jberkel Sep 17, 2017

Author Collaborator

good catch, fixed in dc1b643

}
public func get<V: Value>(_ column: Expression<V?>) -> V? {

public func get<V: Value>(_ column: Expression<V?>) throws -> V? {
func valueAtIndex(_ idx: Int) -> V? {
guard let value = values[idx] as? V.Datatype else { return nil }
return (V.fromDatatypeValue(value) as? V)!
return V.fromDatatypeValue(value) as? V
}

guard let idx = columnNames[column.template] else {
let similar = Array(columnNames.keys).filter { $0.hasSuffix(".\(column.template)") }

switch similar.count {
case 0:
fatalError("no such column '\(column.template)' in columns: \(columnNames.keys.sorted())")
throw QueryError.noSuchColumn(name: column.template, columns: columnNames.keys.sorted())
case 1:
return valueAtIndex(columnNames[similar[0]]!)
default:
fatalError("ambiguous column '\(column.template)' (please disambiguate: \(similar))")
throw QueryError.ambiguousColumn(name: column.template, similar: similar)
}
}

return valueAtIndex(idx)
}

public subscript<T : Value>(column: Expression<T>) -> T {
return get(column)
return try! get(column)
}

public subscript<T : Value>(column: Expression<T?>) -> T? {
return get(column)
return try! get(column)
}
}

Expand Down
14 changes: 14 additions & 0 deletions Tests/SQLiteTests/QueryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -412,4 +412,18 @@ class QueryIntegrationTests : SQLiteTestCase {
let orderedIDs = try db.prepare(query3.union(query4).order(Expression<Int>(literal: "weight"), email)).map { $0[id] }
XCTAssertEqual(Array(expectedIDs.reversed()), orderedIDs)
}

func test_no_such_column() throws {
let doesNotExist = Expression<String>("doesNotExist")
try! InsertUser("alice")
let row = try! db.pluck(users.filter(email == "alice@example.com"))!

XCTAssertThrowsError(try row.get(doesNotExist)) { error in
if case QueryError.noSuchColumn(let name, _) = error {
XCTAssertEqual("\"doesNotExist\"", name)
} else {
XCTFail("unexpected error: \(error)")
}
}
}
}

0 comments on commit bd1fc80

Please sign in to comment.