Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Playgrounds/README.playground/Contents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ try database.execute(sql)
Insert new contacts Paul and John.
*/
let insert = try database.prepare("INSERT INTO contacts (id, name) VALUES (?, ?);")
try insert.bind(parameters: 1, "Paul").execute().reset()
try insert.bind(parameters: 1, "Paul").execute()
try insert.bind(parameters: 2, "John").execute()
/*:
## Select
Expand All @@ -48,19 +48,20 @@ try insert.bind(parameters: 2, "John").execute()
*/
struct Contact: Codable {
let id: Int
let name: String
let name: String?
}

let select = try database.prepare("SELECT * FROM contacts;")
let contacts = try select.array(Contact.self)
print(contacts)
/*:
## DataFrame

The [DataFrame](https://developer.apple.com/documentation/tabulardata/dataframe) from the [TabularData](https://developer.apple.com/documentation/tabulardata) framework is supported.

It can help to print the table.
*/
let df = try database.prepare("SELECT * FROM contacts;").dataFrame()
let df = try select.dataFrame()
print(df)
/*:
```
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ try database.execute(sql)
Insert new contacts Paul and John.
```swift
let insert = try database.prepare("INSERT INTO contacts (id, name) VALUES (?, ?);")
try insert.bind(parameters: 1, "Paul").execute().reset()
try insert.bind(parameters: 1, "Paul").execute()
try insert.bind(parameters: 2, "John").execute()
```
## Select
Expand All @@ -47,19 +47,20 @@ Select all contacts from database.
```swift
struct Contact: Codable {
let id: Int
let name: String
let name: String?
}

let select = try database.prepare("SELECT * FROM contacts;")
let contacts = try select.array(Contact.self)
print(contacts)
```
## DataFrame

The [DataFrame](https://developer.apple.com/documentation/tabulardata/dataframe) from the [TabularData](https://developer.apple.com/documentation/tabulardata) framework is supported.

It can help to print the table.
```swift
let df = try database.prepare("SELECT * FROM contacts;").dataFrame()
let df = try select.dataFrame()
print(df)
```
```
Expand Down
1 change: 1 addition & 0 deletions Sources/SQLyra/DataFrame.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ extension PreparedStatement {
}
var df = DataFrame(columns: columns)
var count = 0
defer { _reset() }
while let row = try row() {
df.appendEmptyRow()
for index in (0..<columnCount) {
Expand Down
9 changes: 8 additions & 1 deletion Sources/SQLyra/PreparedStatement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ public final class PreparedStatement: DatabaseHandle {
/// - Throws: ``DatabaseError``
@discardableResult
public func execute() throws -> PreparedStatement {
try check(sqlite3_step(stmt), SQLITE_DONE)
defer { _reset() }
return try check(sqlite3_step(stmt), SQLITE_DONE)
}

/// Reset the prepared statement.
Expand All @@ -47,6 +48,11 @@ public final class PreparedStatement: DatabaseHandle {
public func reset() throws -> PreparedStatement {
try check(sqlite3_reset(stmt))
}

/// clear error state and prepare for reuse
func _reset() {
sqlite3_reset(stmt)
}
}

// MARK: - Retrieving Statement SQL
Expand Down Expand Up @@ -176,6 +182,7 @@ extension PreparedStatement {
}

public func array<T>(_ type: T.Type, using decoder: RowDecoder) throws -> [T] where T: Decodable {
defer { _reset() }
var array: [T] = []
while let row = try row() {
let value = try row.decode(type, using: decoder)
Expand Down
32 changes: 29 additions & 3 deletions Tests/SQLyraTests/PreparedStatementTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ struct Contact: Codable, Equatable, Sendable {
let rating: Double?
let image: Data?

static let table = "CREATE TABLE contacts (id INT, name TEXT, rating FLOAT, image BLOB);"
static let table = "CREATE TABLE contacts (id INT, name TEXT, rating REAL, image BLOB) STRICT;"
static let insert = "INSERT INTO contacts (id, name, rating, image) VALUES (:id, :name, :rating, :image)"
}

Expand Down Expand Up @@ -71,7 +71,7 @@ struct PreparedStatementTests {
@Test func columns() throws {
let insert = try db.prepare(Contact.insert)

try insert.bind(parameters: 5, "A", 2.0, .null).execute().reset()
try insert.bind(parameters: 5, "A", 2.0, .null).execute()
try insert.bind(parameters: 6, "B", .null, .blob(Data("123".utf8))).execute()

let select = try db.prepare("SELECT * FROM contacts;")
Expand All @@ -94,11 +94,37 @@ struct PreparedStatementTests {
#expect(contracts == expected)
}

@Test func decode() throws {
let insert = try db.prepare(Contact.insert)

try insert.bind(parameters: 5, "A", 2.0, .null).execute()
try insert.bind(parameters: 6, "B", .null, .blob(Data("123".utf8))).execute()

let select = try db.prepare("SELECT * FROM contacts;")
let contracts = try select.array(Contact.self)

let expected = [
Contact(id: 5, name: "A", rating: 2.0, image: nil),
Contact(id: 6, name: "B", rating: nil, image: Data("123".utf8)),
]
#expect(contracts == expected)
}

@Test func execute() throws {
let insert = try db.prepare(Contact.insert)

try insert.bind(name: ":id", parameter: "invalid")
#expect(throws: DatabaseError.self) { try insert.execute() }

try insert.bind(name: ":id", parameter: 4)
#expect(throws: Never.self) { try insert.execute() }
}

@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
@Test func dataFrame() throws {
let insert = try db.prepare(Contact.insert)

try insert.bind(parameters: 5, "A").execute().reset()
try insert.bind(parameters: 5, "A").execute()
try insert.bind(parameters: 6, "B").execute()

let df = try db.prepare("SELECT * FROM contacts;").dataFrame()
Expand Down