Skip to content

Commit 096d382

Browse files
committed
introduced SQLColumnValue, removed convert(from:) from SQLDataType, and used InitializableBySQLColumnValue for SwiftSQLExt conveniences
1 parent 78d3f17 commit 096d382

File tree

6 files changed

+197
-35
lines changed

6 files changed

+197
-35
lines changed

Sources/SwiftSQL/SQLColumnValue.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// The MIT License (MIT)
2+
//
3+
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).
4+
5+
import Foundation
6+
7+
public enum SQLColumnValue {
8+
case int64(Int64)
9+
case double(Double)
10+
case string(String)
11+
case data(Data)
12+
case null
13+
}

Sources/SwiftSQL/SQLDataType.swift

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import SQLite3
1212
public protocol SQLDataType {
1313
func sqlBind(statement: OpaquePointer, index: Int32)
1414
static func sqlColumn(statement: OpaquePointer, index: Int32) -> Self
15-
static func convert(from value: Any) -> Self?
1615
}
1716

1817
extension Int: SQLDataType {
@@ -23,11 +22,6 @@ extension Int: SQLDataType {
2322
public static func sqlColumn(statement: OpaquePointer, index: Int32) -> Int {
2423
Int(sqlite3_column_int64(statement, index))
2524
}
26-
27-
public static func convert(from value: Any) -> Int? {
28-
guard let int64 = value as? Int64 else { return nil }
29-
return Int(int64)
30-
}
3125
}
3226

3327
extension Int32: SQLDataType {
@@ -38,11 +32,6 @@ extension Int32: SQLDataType {
3832
public static func sqlColumn(statement: OpaquePointer, index: Int32) -> Int32 {
3933
sqlite3_column_int(statement, index)
4034
}
41-
42-
public static func convert(from value: Any) -> Self? {
43-
guard let int64 = value as? Int64 else { return nil }
44-
return Int32(int64)
45-
}
4635
}
4736

4837
extension Int64: SQLDataType {
@@ -53,8 +42,6 @@ extension Int64: SQLDataType {
5342
public static func sqlColumn(statement: OpaquePointer, index: Int32) -> Int64 {
5443
sqlite3_column_int64(statement, index)
5544
}
56-
57-
public static func convert(from value: Any) -> Self? { value as? Self }
5845
}
5946

6047
extension Double: SQLDataType {
@@ -65,8 +52,6 @@ extension Double: SQLDataType {
6552
public static func sqlColumn(statement: OpaquePointer, index: Int32) -> Double {
6653
sqlite3_column_double(statement, index)
6754
}
68-
69-
public static func convert(from value: Any) -> Self? { value as? Self }
7055
}
7156

7257
extension String: SQLDataType {
@@ -78,8 +63,6 @@ extension String: SQLDataType {
7863
guard let pointer = sqlite3_column_text(statement, index) else { return "" }
7964
return String(cString: pointer)
8065
}
81-
82-
public static func convert(from value: Any) -> Self? { value as? Self }
8366
}
8467

8568
extension Data: SQLDataType {
@@ -94,8 +77,6 @@ extension Data: SQLDataType {
9477
let count = Int(sqlite3_column_bytes(statement, Int32(index)))
9578
return Data(bytes: pointer, count: count)
9679
}
97-
98-
public static func convert(from value: Any) -> Self? { value as? Self }
9980
}
10081

10182
private let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)

Sources/SwiftSQL/SQLStatement.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -224,25 +224,25 @@ public final class SQLStatement {
224224
}
225225
}
226226

227-
public func column(at index: Int) -> Any? {
227+
public func column(at index: Int) -> SQLColumnValue {
228228
let index = Int32(index)
229229
let type = sqlite3_column_type(ref, index)
230230
switch type {
231231
case SQLITE_INTEGER:
232-
return sqlite3_column_int64(ref, index)
232+
return .int64(sqlite3_column_int64(ref, index))
233233
case SQLITE_FLOAT:
234-
return sqlite3_column_double(ref, index)
234+
return .double(sqlite3_column_double(ref, index))
235235
case SQLITE_TEXT:
236-
return String(cString: sqlite3_column_text(ref, index))
236+
return .string(String(cString: sqlite3_column_text(ref, index)))
237237
case SQLITE_BLOB:
238238
if let bytes = sqlite3_column_blob(ref, index) {
239239
let byteCount = sqlite3_column_bytes(ref, index)
240-
return Data(bytes: bytes, count: Int(byteCount))
240+
return .data(Data(bytes: bytes, count: Int(byteCount)))
241241
} else {
242-
return Data()
242+
return .data(Data())
243243
}
244244
default:
245-
return nil
245+
return .null
246246
}
247247
}
248248

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// The MIT License (MIT)
2+
//
3+
// Copyright (c) 2020 Alexander Grebenyuk (github.com/kean).
4+
5+
import Foundation
6+
import SwiftSQL
7+
8+
// Conveniences for commonly used types.
9+
// Note that automatc type conversion is being done;
10+
// this is to maintain the expected type flexibility
11+
// of SQLite.
12+
// From the docs:
13+
// >If the result column is not initially in the requested format (for example, if the query returns an integer but the sqlite3_column_text() interface is used to extract the value) then an automatic type conversion is performed.
14+
// Reference: https://www.sqlite.org/c3ref/column_blob.html
15+
16+
extension Int: InitializableBySQLColumnValue {
17+
public init?(sqlColumnValue: SQLColumnValue) {
18+
switch sqlColumnValue {
19+
case .int64(let int64):
20+
self = Int(int64)
21+
case .double(let double):
22+
self = Int(double)
23+
case .string(let string):
24+
if let int = Int(string) {
25+
self = int
26+
} else {
27+
return nil
28+
}
29+
default:
30+
return nil
31+
}
32+
}
33+
}
34+
35+
extension Int32: InitializableBySQLColumnValue {
36+
public init?(sqlColumnValue: SQLColumnValue) {
37+
switch sqlColumnValue {
38+
case .int64(let int64):
39+
self = Int32(int64)
40+
case .double(let double):
41+
self = Int32(double)
42+
case .string(let string):
43+
if let int32 = Int32(string) {
44+
self = int32
45+
} else {
46+
return nil
47+
}
48+
default:
49+
return nil
50+
}
51+
}
52+
}
53+
54+
extension Int64: InitializableBySQLColumnValue {
55+
public init?(sqlColumnValue: SQLColumnValue) {
56+
switch sqlColumnValue {
57+
case .int64(let int64):
58+
self = int64
59+
case .double(let double):
60+
self = Int64(double)
61+
case .string(let string):
62+
if let int64 = Int64(string) {
63+
self = int64
64+
} else {
65+
return nil
66+
}
67+
default:
68+
return nil
69+
}
70+
}
71+
}
72+
73+
extension Double: InitializableBySQLColumnValue {
74+
public init?(sqlColumnValue: SQLColumnValue) {
75+
switch sqlColumnValue {
76+
case .int64(let int64):
77+
self = Double(int64)
78+
case .double(let double):
79+
self = double
80+
case .string(let string):
81+
if let double = Double(string) {
82+
self = double
83+
} else {
84+
return nil
85+
}
86+
default:
87+
return nil
88+
}
89+
}
90+
}
91+
92+
extension String: InitializableBySQLColumnValue {
93+
public init?(sqlColumnValue: SQLColumnValue) {
94+
switch sqlColumnValue {
95+
case .int64(let int64):
96+
self = String(int64)
97+
case .double(let double):
98+
self = String(double)
99+
case .string(let string):
100+
self = string
101+
default:
102+
return nil
103+
}
104+
}
105+
}
106+
107+
extension Data: InitializableBySQLColumnValue {
108+
public init?(sqlColumnValue: SQLColumnValue) {
109+
switch sqlColumnValue {
110+
case .data(let data):
111+
self = data
112+
default:
113+
return nil
114+
}
115+
}
116+
}

Sources/SwiftSQLExt/SwiftSQLExt.swift

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,9 @@ public struct SQLRow {
7070
/// column index is out of range, the result is undefined.
7171
///
7272
/// - parameter index: The leftmost column of the result set has the index 0.
73-
public subscript<T: SQLDataType>(index: Int) -> T {
74-
let value = values[index]!
75-
guard let convertedValue = T.convert(from: value) else {
73+
public subscript<T: InitializableBySQLColumnValue>(index: Int) -> T {
74+
let value = values[index]
75+
guard let convertedValue = T(sqlColumnValue: value) else {
7676
fatalError("Could not convert \(type(of: value)). Make sure target type (\(T.self)) correctly implements convert(from:).")
7777
}
7878
return convertedValue
@@ -85,9 +85,8 @@ public struct SQLRow {
8585
/// column index is out of range, the result is undefined.
8686
///
8787
/// - parameter index: The leftmost column of the result set has the index 0.
88-
public subscript<T: SQLDataType>(index: Int) -> T? {
89-
guard let value = values[index] else { return nil }
90-
return T.convert(from: value)
88+
public subscript<T: InitializableBySQLColumnValue>(index: Int) -> T? {
89+
return T(sqlColumnValue: values[index])
9190
}
9291

9392
/// Returns a single column (by its name) of the current result row of a query.
@@ -96,7 +95,7 @@ public struct SQLRow {
9695
/// If the passed columnName doesn't point to a valid column name, a fatal error is raised.
9796
///
9897
/// - parameter columnName: The name of the column.
99-
public subscript<T: SQLDataType>(columnName: String) -> T {
98+
public subscript<T: InitializableBySQLColumnValue>(columnName: String) -> T {
10099
guard let columnIndex = columnIndicesByNames[columnName] else {
101100
fatalError("No such column \(columnName)")
102101
}
@@ -109,17 +108,21 @@ public struct SQLRow {
109108
/// If the passed columnName doesn't point to a valid column name, nil is returned.
110109
///
111110
/// - parameter columnName: The name of the column.
112-
public subscript<T: SQLDataType>(columnName: String) -> T? {
111+
public subscript<T: InitializableBySQLColumnValue>(columnName: String) -> T? {
113112
guard let columnIndex = columnIndicesByNames[columnName] else {
114113
return nil
115114
}
116115
return self[columnIndex]
117116
}
118117

119-
private let values: [Any?]
118+
private let values: [SQLColumnValue]
120119
private let columnIndicesByNames: [String : Int]
121120
}
122121

123122
public protocol SQLRowDecodable {
124123
init(row: SQLRow) throws
125124
}
125+
126+
public protocol InitializableBySQLColumnValue {
127+
init?(sqlColumnValue: SQLColumnValue)
128+
}

Tests/SwiftSQLExtTests/SwiftSQLExtTests.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,56 @@ final class SwiftSQLExtTests: XCTestCase {
113113
XCTAssertNil(try XCTUnwrap(row)["Level"] as Int?)
114114
}
115115

116+
func testInitializableBySQLColumnValueConveniencesIn64Source() throws {
117+
// GIVEN
118+
let sqlColumnValue = SQLColumnValue.int64(1)
119+
120+
// WHEN
121+
let int32 = Int32(sqlColumnValue: sqlColumnValue)
122+
let int64 = Int64(sqlColumnValue: sqlColumnValue)
123+
let double = Double(sqlColumnValue: sqlColumnValue)
124+
let string = String(sqlColumnValue: sqlColumnValue)
125+
126+
//THEN
127+
XCTAssertEqual(try XCTUnwrap(int32), 1)
128+
XCTAssertEqual(try XCTUnwrap(int64), 1)
129+
XCTAssertEqual(try XCTUnwrap(double), 1)
130+
XCTAssertEqual(try XCTUnwrap(string), "1")
131+
}
132+
133+
func testInitializableBySQLColumnValueConveniencesDoubleSource() throws {
134+
// GIVEN
135+
let sqlColumnValue = SQLColumnValue.double(1)
136+
137+
// WHEN
138+
let int = Int(sqlColumnValue: sqlColumnValue)
139+
let int32 = Int32(sqlColumnValue: sqlColumnValue)
140+
let int64 = Int64(sqlColumnValue: sqlColumnValue)
141+
let string = String(sqlColumnValue: sqlColumnValue)
142+
143+
//THEN
144+
XCTAssertEqual(try XCTUnwrap(int), 1)
145+
XCTAssertEqual(try XCTUnwrap(int32), 1)
146+
XCTAssertEqual(try XCTUnwrap(int64), 1)
147+
XCTAssertEqual(try XCTUnwrap(string), "1.0")
148+
}
116149

150+
func testInitializableBySQLColumnValueConveniencesStringSource() throws {
151+
// GIVEN
152+
let sqlColumnValue = SQLColumnValue.string("1")
153+
154+
// WHEN
155+
let int = Int(sqlColumnValue: sqlColumnValue)
156+
let int32 = Int32(sqlColumnValue: sqlColumnValue)
157+
let int64 = Int64(sqlColumnValue: sqlColumnValue)
158+
let double = Double(sqlColumnValue: sqlColumnValue)
159+
160+
//THEN
161+
XCTAssertEqual(try XCTUnwrap(int), 1)
162+
XCTAssertEqual(try XCTUnwrap(int32), 1)
163+
XCTAssertEqual(try XCTUnwrap(int64), 1)
164+
XCTAssertEqual(try XCTUnwrap(double), 1)
165+
}
117166
}
118167

119168
private extension SQLConnection {

0 commit comments

Comments
 (0)