Skip to content

Commit a64727d

Browse files
committed
Implement bind by name
1 parent 57d789e commit a64727d

File tree

3 files changed

+73
-12
lines changed

3 files changed

+73
-12
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ VALUES (?, ?)
3232
2. Bind values to parameters using one of the `bind()` methods. The provided values must be one of the data types supported by SQLite (see `SQLDataType` for more info)
3333

3434
```swift
35-
try statement.bind("Alexander", "Grebenyuk")
35+
try statement.bind("John", "Appleseed")
3636
```
3737

3838
3. Execute the statement.

Sources/SwiftSQL/SQLStatement.swift

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ public final class SQLStatement {
6464

6565
// MARK: Execute
6666

67+
#warning("TODO: add example")
68+
6769
/// Executes the statement and returns `true` if the next row is available.
6870
/// Returns `false` if the statement is finished executing and no more data
6971
/// is available. Throws an error if an error is encountered.
@@ -74,6 +76,7 @@ public final class SQLStatement {
7476
return SQLRow(statement: self)
7577
}
7678

79+
#warning("TODO: document")
7780
@discardableResult
7881
public func execute() throws -> SQLStatement {
7982
try isOK(sqlite3_step(ref))
@@ -82,14 +85,24 @@ public final class SQLStatement {
8285

8386
// MARK: Binding Parameters
8487

85-
/// Binds values to the SQL statement parameters.
88+
/// Binds values to the statement parameters.
89+
///
90+
/// try db.statement("INSERT INTO Users (Level, Name) VALUES (?, ?)")
91+
/// .bind(80, "John")
92+
/// .execute()
93+
///
8694
@discardableResult
8795
public func bind(_ parameters: SQLDataType?...) throws -> Self {
8896
try bind(parameters)
8997
return self
9098
}
9199

92-
/// Binds values to the SQL statement parameters.
100+
/// Binds values to the statement parameters.
101+
///
102+
/// try db.statement("INSERT INTO Users (Level, Name) VALUES (?, ?)")
103+
/// .bind([80, "John"])
104+
/// .execute()
105+
///
93106
@discardableResult
94107
public func bind(_ parameters: [SQLDataType?]) throws -> Self {
95108
for (index, value) in zip(parameters.indices, parameters) {
@@ -98,23 +111,37 @@ public final class SQLStatement {
98111
return self
99112
}
100113

101-
/// Binds values to the SQL statement parameters.
114+
/// Binds values to the named statement parameters.
115+
///
116+
/// let row = try db.statement("SELECT Level, Name FROM Users WHERE Name = :param LIMIT 1")
117+
/// .bind([":param": "John""])
118+
/// .next()
119+
///
120+
/// - parameter name: The name of the parameter. If the name is missing, throws
121+
/// an error.
102122
@discardableResult
103123
public func bind(_ parameters: [String: SQLDataType?]) throws -> Self {
104-
for (name, value) in parameters {
105-
try _bind(value, for: name)
124+
for (key, value) in parameters {
125+
try _bind(value, for: key)
106126
}
107127
return self
108128
}
109129

110130
/// Binds values to the parameter with the given name.
111131
///
112-
/// - parameter name:
132+
/// let row = try db.statement("SELECT Level, Name FROM Users WHERE Name = :param LIMIT 1")
133+
/// .bind("John", for: ":param")
134+
/// .next()
135+
///
136+
/// - parameter name: The name of the parameter. If the name is missing, throws
137+
/// an error.
113138
@discardableResult
114139
public func bind<T: SQLDataType>(_ value: T?, for name: String) throws -> Self {
115140
let index = sqlite3_bind_parameter_index(ref, name)
116-
guard index > 0 else { fatalError("Parameter not found: \(name)") }
117-
try bind(value, at: Int(index))
141+
guard index > 0 else {
142+
throw SQLError(code: SQLITE_MISUSE, message: "Failed to find parameter named \(name)")
143+
}
144+
try bind(value, at: Int(index - 1))
118145
return self
119146
}
120147

@@ -134,8 +161,10 @@ public final class SQLStatement {
134161

135162
private func _bind(_ value: SQLDataType?, for name: String) throws {
136163
let index = sqlite3_bind_parameter_index(ref, name)
137-
guard index > 0 else { fatalError("Parameter not found: \(name)") }
138-
try _bind(value, at: Int32(index))
164+
guard index > 0 else {
165+
throw SQLError(code: SQLITE_MISUSE, message: "Failed to find parameter named \(name)")
166+
}
167+
try _bind(value, at: index - 1)
139168
}
140169

141170
private func _bind(_ value: SQLDataType?, at index: Int32) throws {

Tests/SwiftSQLTests/SQLStatementTests.swift

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,39 @@ final class SQLStatementTests: XCTestCase {
162162
}
163163

164164
func testBindByName() throws {
165-
#warning("TODO: implement")
165+
/// GIVEN
166+
try db.createTables()
167+
168+
try db.statement("INSERT INTO Users (Level, Name) VALUES (?, ?)")
169+
.bind(80, "Alex")
170+
.execute()
171+
172+
// WHEN
173+
let row = try XCTUnwrap(db
174+
.statement("SELECT Level, Name FROM Users WHERE Name = :param")
175+
.bind("Alex", for: ":param")
176+
.next())
177+
178+
// THEN
179+
XCTAssertEqual(row[0], 80)
180+
}
181+
182+
func testBindByNameDictionary() throws {
183+
/// GIVEN
184+
try db.createTables()
185+
186+
try db.statement("INSERT INTO Users (Level, Name) VALUES (?, ?)")
187+
.bind(80, "Alex")
188+
.execute()
189+
190+
// WHEN
191+
let row = try XCTUnwrap(db
192+
.statement("SELECT Level, Name FROM Users WHERE Name = :param")
193+
.bind([":param": "Alex"])
194+
.next())
195+
196+
// THEN
197+
XCTAssertEqual(row[0], 80)
166198
}
167199

168200
func testClearBinding() throws {

0 commit comments

Comments
 (0)