Skip to content

Commit 3df3ed7

Browse files
committed
Avoid circular reference
The REPL particularly hates this. This does mean, however, that `step()` needs to live on the statement itself, not the cursor. In fact, cursor may be better accessed as `row`, so let's do just that: while stmt.step() { let id = stmt.row[0] as Int // ... } Signed-off-by: Stephen Celis <[email protected]>
1 parent 76ad0d8 commit 3df3ed7

File tree

2 files changed

+26
-23
lines changed

2 files changed

+26
-23
lines changed

SQLite Tests/StatementTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,13 +172,13 @@ class StatementTests: XCTestCase {
172172
XCTAssertEqual(6, count)
173173
}
174174

175-
func test_cursor_returnsValues() {
175+
func test_row_returnsValues() {
176176
InsertUser(db, "alice")
177177
let stmt = db.prepare("SELECT id, email FROM users")
178-
stmt.cursor.step()
178+
stmt.step()
179179

180-
XCTAssertEqual(Int64(1), stmt.cursor[0] as Int64)
181-
XCTAssertEqual("[email protected]", stmt.cursor[1] as String)
180+
XCTAssertEqual(Int64(1), stmt.row[0] as Int64)
181+
XCTAssertEqual("[email protected]", stmt.row[1] as String)
182182
}
183183

184184
}

SQLite/Statement.swift

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public final class Statement {
3232

3333
private let database: Database
3434

35-
public lazy var cursor: Cursor = { Cursor(self) }()
35+
public lazy var row: Cursor = { Cursor(self) }()
3636

3737
internal init(_ database: Database, _ SQL: String) {
3838
self.database = database
@@ -115,7 +115,7 @@ public final class Statement {
115115
public func run(bindings: Binding?...) -> Statement {
116116
if !bindings.isEmpty { return run(bindings) }
117117
reset(clearBindings: false)
118-
while cursor.step() {}
118+
while step() {}
119119
return self
120120
}
121121

@@ -142,8 +142,8 @@ public final class Statement {
142142
public func scalar(bindings: Binding?...) -> Binding? {
143143
if !bindings.isEmpty { return scalar(bindings) }
144144
reset(clearBindings: false)
145-
cursor.step()
146-
return cursor[0]
145+
step()
146+
return row[0]
147147
}
148148

149149
/// :param: bindings A list of parameters to bind to the statement.
@@ -163,6 +163,11 @@ public final class Statement {
163163

164164
// MARK: -
165165

166+
public func step() -> Bool {
167+
try(sqlite3_step(handle))
168+
return status == SQLITE_ROW
169+
}
170+
166171
private func reset(clearBindings: Bool = true) {
167172
(status, reason) = (SQLITE_OK, nil)
168173
sqlite3_reset(handle)
@@ -209,7 +214,7 @@ extension Statement: GeneratorType {
209214

210215
/// :returns: The next row from the result set (or nil).
211216
public func next() -> [Binding?]? {
212-
return cursor.step() ? Array(cursor) : nil
217+
return step() ? Array(row) : nil
213218
}
214219

215220
}
@@ -236,33 +241,31 @@ public func || (lhs: Statement, rhs: @autoclosure () -> Statement) -> Statement
236241
/// Cursors provide direct access to a statement's current row.
237242
public struct Cursor {
238243

239-
private unowned let statement: Statement
244+
private let handle: COpaquePointer
240245

241-
private init(_ statement: Statement) {
242-
self.statement = statement
243-
}
246+
private let columnCount: Int
244247

245-
public func step() -> Bool {
246-
statement.try(sqlite3_step(statement.handle))
247-
return statement.status == SQLITE_ROW
248+
private init(_ statement: Statement) {
249+
handle = statement.handle
250+
columnCount = statement.columnCount
248251
}
249252

250253
public subscript(idx: Int) -> Blob {
251-
let bytes = sqlite3_column_blob(statement.handle, Int32(idx))
252-
let length = sqlite3_column_bytes(statement.handle, Int32(idx))
254+
let bytes = sqlite3_column_blob(handle, Int32(idx))
255+
let length = sqlite3_column_bytes(handle, Int32(idx))
253256
return Blob(bytes: bytes, length: Int(length))
254257
}
255258

256259
public subscript(idx: Int) -> Double {
257-
return sqlite3_column_double(statement.handle, Int32(idx))
260+
return sqlite3_column_double(handle, Int32(idx))
258261
}
259262

260263
public subscript(idx: Int) -> Int64 {
261-
return sqlite3_column_int64(statement.handle, Int32(idx))
264+
return sqlite3_column_int64(handle, Int32(idx))
262265
}
263266

264267
public subscript(idx: Int) -> String {
265-
return String.fromCString(UnsafePointer(sqlite3_column_text(statement.handle, Int32(idx)))) ?? ""
268+
return String.fromCString(UnsafePointer(sqlite3_column_text(handle, Int32(idx)))) ?? ""
266269
}
267270

268271
public subscript(idx: Int) -> Bool {
@@ -279,7 +282,7 @@ public struct Cursor {
279282
extension Cursor: SequenceType {
280283

281284
public subscript(idx: Int) -> Binding? {
282-
switch sqlite3_column_type(statement.handle, Int32(idx)) {
285+
switch sqlite3_column_type(handle, Int32(idx)) {
283286
case SQLITE_BLOB:
284287
return self[idx] as Blob
285288
case SQLITE_FLOAT:
@@ -298,7 +301,7 @@ extension Cursor: SequenceType {
298301
public func generate() -> GeneratorOf<Binding?> {
299302
var idx = 0
300303
return GeneratorOf<Binding?> {
301-
idx >= self.statement.columnCount ? Optional<Binding?>.None : self[idx++]
304+
idx >= self.columnCount ? Optional<Binding?>.None : self[idx++]
302305
}
303306
}
304307

0 commit comments

Comments
 (0)