Skip to content

Commit 35066a6

Browse files
committed
BLOB support
Via a new struct, Blob. Blob, like NSData, exposes `bytes` and `length` (and actually uses NSData under the hood, for the sake of memory management). Without any extensions, you can use Blob's public interface, though it's slightly verbose: let data = UIImagePNGRepresentation(image) let blob = Blob(bytes: data.bytes, length: data.length) users.insert(avatar <- blob) Simple extension simplifies: extension Blob { init(_ data: NSData) { self.init(bytes: data.bytes, length: data.length) } public var data -> NSData { return NSData(bytes: bytes, length: length) } } users.insert(avatar <- Blob(UIImagePNGRepresentation(image))) // INSERT INTO users (avatar) VALUES (x'...') You can, of course, extend further to suit your needs: extension Blob { init(_ image: UIImage) { self.init(UIImagePNGRepresentation(image)) } public var image -> UIImage { return UIImage(data: data) } } users.insert(avatar <- Blob(image)) // INSERT INTO users (avatar) VALUES (x'...') Signed-off-by: Stephen Celis <stephen@stephencelis.com>
1 parent 40b05b2 commit 35066a6

File tree

4 files changed

+91
-38
lines changed

4 files changed

+91
-38
lines changed

SQLite Common Tests/StatementTests.swift

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,42 @@ class StatementTests: XCTestCase {
1212
}
1313

1414
func test_bind_withVariadicParameters_bindsParameters() {
15-
let stmt = db.prepare("SELECT ?, ?, ?, ?")
16-
ExpectExecutions(db, ["SELECT 0, 1, 2.0, '3'": 1]) { _ in
17-
stmt.bind(false, 1, 2.0, "3").run()
18-
return
15+
let stmt = db.prepare("SELECT ?, ?, ?, ?, ?")
16+
ExpectExecutions(db, ["SELECT 0, 1, 2.0, '3', x'34'": 1]) { _ in
17+
withBlob { blob in
18+
stmt.bind(false, 1, 2.0, "3", blob).run()
19+
return
20+
}
1921
}
2022
}
2123

2224
func test_bind_withArrayOfParameters_bindsParameters() {
23-
let stmt = db.prepare("SELECT ?, ?, ?, ?")
24-
ExpectExecutions(db, ["SELECT 0, 1, 2.0, '3'": 1]) { _ in
25-
stmt.bind([false, 1, 2.0, "3"]).run()
26-
return
25+
let stmt = db.prepare("SELECT ?, ?, ?, ?, ?")
26+
ExpectExecutions(db, ["SELECT 0, 1, 2.0, '3', x'34'": 1]) { _ in
27+
withBlob { blob in
28+
stmt.bind([false, 1, 2.0, "3", blob]).run()
29+
return
30+
}
2731
}
2832
}
2933

3034
func test_bind_withNamedParameters_bindsParameters() {
31-
let stmt = db.prepare("SELECT ?1, ?2, ?3, ?4")
32-
ExpectExecutions(db, ["SELECT 0, 1, 2.0, '3'": 1]) { _ in
33-
stmt.bind(["?1": false, "?2": 1, "?3": 2.0, "?4": "3"]).run()
34-
return
35+
let stmt = db.prepare("SELECT ?1, ?2, ?3, ?4, ?5")
36+
ExpectExecutions(db, ["SELECT 0, 1, 2.0, '3', x'34'": 1]) { _ in
37+
withBlob { blob in
38+
stmt.bind(["?1": false, "?2": 1, "?3": 2.0, "?4": "3", "?5": blob]).run()
39+
return
40+
}
41+
}
42+
}
43+
44+
func test_bind_withBlob_bindsBlob() {
45+
let stmt = db.prepare("SELECT ?")
46+
ExpectExecutions(db, ["SELECT x'34'": 1]) { _ in
47+
withBlob { blob in
48+
stmt.bind(blob).run()
49+
return
50+
}
3551
}
3652
}
3753

@@ -183,3 +199,12 @@ class StatementTests: XCTestCase {
183199
}
184200

185201
}
202+
203+
func withBlob(block: Blob -> ()) {
204+
let length = 1
205+
let buflen = Int(length) + 1
206+
let buffer = UnsafeMutablePointer<()>.alloc(buflen)
207+
memcpy(buffer, "4", UInt(length))
208+
block(Blob(bytes: buffer, length: length))
209+
buffer.dealloc(buflen)
210+
}

SQLite Common/Query.swift

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,9 @@ public struct Query {
289289

290290
// FIXME: rdar://18673897 subscript<T>(expression: Expression<V>) -> Expression<V>
291291

292+
public subscript(column: Expression<Blob>) -> Expression<Blob> { return namespace(column) }
293+
public subscript(column: Expression<Blob?>) -> Expression<Blob?> { return namespace(column) }
294+
292295
public subscript(column: Expression<Bool>) -> Expression<Bool> { return namespace(column) }
293296
public subscript(column: Expression<Bool?>) -> Expression<Bool?> { return namespace(column) }
294297

@@ -642,35 +645,18 @@ public struct Row {
642645

643646
// FIXME: rdar://18673897 subscript<T>(expression: Expression<V>) -> Expression<V>
644647

645-
/// Returns a row’s value for the given column.
646-
///
647-
/// :param: column An expression representing a column selected in a Query.
648-
///
649-
/// returns The value for the given column.
648+
public subscript(column: Expression<Blob>) -> Blob { return get(column) }
649+
public subscript(column: Expression<Blob?>) -> Blob? { return get(column) }
650+
650651
public subscript(column: Expression<Bool>) -> Bool { return get(column) }
651652
public subscript(column: Expression<Bool?>) -> Bool? { return get(column) }
652653

653-
/// Returns a row’s value for the given column.
654-
///
655-
/// :param: column An expression representing a column selected in a Query.
656-
///
657-
/// returns The value for the given column.
658654
public subscript(column: Expression<Double>) -> Double { return get(column) }
659655
public subscript(column: Expression<Double?>) -> Double? { return get(column) }
660656

661-
/// Returns a row’s value for the given column.
662-
///
663-
/// :param: column An expression representing a column selected in a Query.
664-
///
665-
/// returns The value for the given column.
666657
public subscript(column: Expression<Int>) -> Int { return get(column) }
667658
public subscript(column: Expression<Int?>) -> Int? { return get(column) }
668659

669-
/// Returns a row’s value for the given column.
670-
///
671-
/// :param: column An expression representing a column selected in a Query.
672-
///
673-
/// returns The value for the given column.
674660
public subscript(column: Expression<String>) -> String { return get(column) }
675661
public subscript(column: Expression<String?>) -> String? { return get(column) }
676662

SQLite Common/Statement.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ public final class Statement {
8484
}
8585

8686
private func bind(value: Binding?, atIndex idx: Int) {
87-
if let value = value as? Bool {
87+
if let value = value as? Blob {
88+
try(sqlite3_bind_blob(handle, Int32(idx), value.bytes, Int32(value.length), SQLITE_TRANSIENT))
89+
} else if let value = value as? Bool {
8890
bind(value ? 1 : 0, atIndex: idx)
8991
} else if let value = value as? Double {
9092
try(sqlite3_bind_double(handle, Int32(idx), value))
@@ -213,6 +215,10 @@ extension Statement: GeneratorType {
213215
var row = Element()
214216
for idx in 0..<columnNames.count {
215217
switch sqlite3_column_type(handle, Int32(idx)) {
218+
case SQLITE_BLOB:
219+
let bytes = sqlite3_column_blob(handle, Int32(idx))
220+
let length = sqlite3_column_bytes(handle, Int32(idx))
221+
row.append(Blob(bytes: bytes, length: Int(length)))
216222
case SQLITE_FLOAT:
217223
row.append(Double(sqlite3_column_double(handle, Int32(idx))))
218224
case SQLITE_INTEGER:

SQLite Common/Value.swift

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@
2121
// THE SOFTWARE.
2222
//
2323

24+
import Foundation
25+
2426
public protocol Binding {}
2527

28+
public protocol Number: Binding {}
29+
2630
public protocol Value {
2731

2832
typealias ValueType = Self
@@ -37,7 +41,39 @@ public protocol Value {
3741

3842
}
3943

40-
public protocol Number: Binding {}
44+
public struct Blob {
45+
46+
public let data: NSData
47+
48+
public var bytes: UnsafePointer<()> {
49+
return data.bytes
50+
}
51+
52+
public var length: Int {
53+
return data.length
54+
}
55+
56+
public init(bytes: UnsafePointer<()>, length: Int) {
57+
data = NSData(bytes: bytes, length: length)
58+
}
59+
60+
}
61+
62+
extension Blob: Binding, Value {
63+
64+
public typealias Datatype = Blob
65+
66+
public static var declaredDatatype = "BLOB"
67+
68+
public static func fromDatatypeValue(datatypeValue: Datatype) -> Blob {
69+
return datatypeValue
70+
}
71+
72+
public var datatypeValue: Datatype {
73+
return self
74+
}
75+
76+
}
4177

4278
extension Bool: Binding, Value {
4379

@@ -46,7 +82,7 @@ extension Bool: Binding, Value {
4682
public static var declaredDatatype = "BOOLEAN"
4783

4884
public static func fromDatatypeValue(datatypeValue: Datatype) -> Bool {
49-
return self(datatypeValue)
85+
return datatypeValue
5086
}
5187

5288
public var datatypeValue: Datatype {
@@ -62,7 +98,7 @@ extension Double: Number, Value {
6298
public static var declaredDatatype = "REAL"
6399

64100
public static func fromDatatypeValue(datatypeValue: Datatype) -> Double {
65-
return self(datatypeValue)
101+
return datatypeValue
66102
}
67103

68104
public var datatypeValue: Datatype {
@@ -78,7 +114,7 @@ extension Int: Number, Value {
78114
public static var declaredDatatype = "INTEGER"
79115

80116
public static func fromDatatypeValue(datatypeValue: Datatype) -> Int {
81-
return self(datatypeValue)
117+
return datatypeValue
82118
}
83119

84120
public var datatypeValue: Datatype {
@@ -94,7 +130,7 @@ extension String: Binding, Value {
94130
public static var declaredDatatype = "TEXT"
95131

96132
public static func fromDatatypeValue(datatypeValue: Datatype) -> String {
97-
return self(datatypeValue)
133+
return datatypeValue
98134
}
99135

100136
public var datatypeValue: Datatype {

0 commit comments

Comments
 (0)