Skip to content

Commit d926517

Browse files
committed
adds support for extended error codes
1 parent f06b8df commit d926517

File tree

5 files changed

+60
-1
lines changed

5 files changed

+60
-1
lines changed

Sources/SQLite/Core/Connection.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,13 @@ public final class Connection {
156156
Int(sqlite3_total_changes(handle))
157157
}
158158

159+
/// Whether or not the database will return extended error codes when errors are handled.
160+
public var usesExtendedErrorCodes: Bool = false {
161+
didSet {
162+
sqlite3_extended_result_codes(handle, usesExtendedErrorCodes ? 1 : 0)
163+
}
164+
}
165+
159166
// MARK: - Execute
160167

161168
/// Executes a batch of SQL statements.

Sources/SQLite/Core/Result.swift

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,27 @@ public enum Result: Error {
2121
/// - statement: the statement which produced the error
2222
case error(message: String, code: Int32, statement: Statement?)
2323

24+
/// Represents a SQLite specific [extended error code] (https://sqlite.org/rescode.html#primary_result_codes_versus_extended_result_codes)
25+
///
26+
/// - message: English-language text that describes the error
27+
///
28+
/// - extendedCode: SQLite [extended error code](https://sqlite.org/rescode.html#extended_result_code_list)
29+
///
30+
/// - statement: the statement which produced the error
31+
case extendedError(message: String, extendedCode: Int32, statement: Statement?)
32+
2433
init?(errorCode: Int32, connection: Connection, statement: Statement? = nil) {
2534
guard !Result.successCodes.contains(errorCode) else { return nil }
2635

2736
let message = String(cString: sqlite3_errmsg(connection.handle))
28-
self = .error(message: message, code: errorCode, statement: statement)
37+
38+
guard connection.usesExtendedErrorCodes else {
39+
self = .error(message: message, code: errorCode, statement: statement)
40+
return
41+
}
42+
43+
let extendedErrorCode = sqlite3_extended_errcode(connection.handle)
44+
self = .extendedError(message: message, extendedCode: extendedErrorCode, statement: statement)
2945
}
3046

3147
}
@@ -40,6 +56,12 @@ extension Result: CustomStringConvertible {
4056
} else {
4157
return "\(message) (code: \(errorCode))"
4258
}
59+
case let .extendedError(message, extendedCode, statement):
60+
if let statement = statement {
61+
return "\(message) (\(statement)) (extended code: \(extendedCode))"
62+
} else {
63+
return "\(message) (extended code: \(extendedCode))"
64+
}
4365
}
4466
}
4567
}

Tests/SQLiteTests/Core/ConnectionTests.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ class ConnectionTests: SQLiteTestCase {
111111
XCTAssertEqual(2, db.totalChanges)
112112
}
113113

114+
func test_useExtendedErrorCodes_returnsFalseDefault() throws {
115+
XCTAssertFalse(db.usesExtendedErrorCodes)
116+
}
117+
114118
func test_prepare_preparesAndReturnsStatements() throws {
115119
_ = try db.prepare("SELECT * FROM users WHERE admin = 0")
116120
_ = try db.prepare("SELECT * FROM users WHERE admin = ?", 0)

Tests/SQLiteTests/Core/ResultTests.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,17 @@ class ResultTests: XCTestCase {
5353
XCTAssertEqual("not an error (SELECT 1) (code: 21)",
5454
Result(errorCode: SQLITE_MISUSE, connection: connection, statement: statement)?.description)
5555
}
56+
57+
func test_init_extended_with_other_code_returns_error() {
58+
connection.usesExtendedErrorCodes = true
59+
if case .some(.extendedError(let message, let extendedCode, let statement)) =
60+
Result(errorCode: SQLITE_MISUSE, connection: connection, statement: nil) {
61+
XCTAssertEqual("not an error", message)
62+
XCTAssertEqual(extendedCode, 0)
63+
XCTAssertNil(statement)
64+
XCTAssert(connection === connection)
65+
} else {
66+
XCTFail("no error")
67+
}
68+
}
5669
}

Tests/SQLiteTests/Typed/QueryIntegrationTests.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,19 @@ class QueryIntegrationTests: SQLiteTestCase {
229229
}
230230
}
231231

232+
func test_extendedErrorCodes_catchConstraintError() throws {
233+
db.usesExtendedErrorCodes = true
234+
try db.run(users.insert(email <- "alice@example.com"))
235+
do {
236+
try db.run(users.insert(email <- "alice@example.com"))
237+
XCTFail("expected error")
238+
} catch let Result.extendedError(_, extendedCode, _) where extendedCode == 2_067 {
239+
// SQLITE_CONSTRAINT_UNIQUE expected
240+
} catch let error {
241+
XCTFail("unexpected error: \(error)")
242+
}
243+
}
244+
232245
// https://github.com/stephencelis/SQLite.swift/issues/285
233246
func test_order_by_random() throws {
234247
try insertUsers(["a", "b", "c'"])

0 commit comments

Comments
 (0)