diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift index a6199204..d202c564 100644 --- a/Sources/SQLite/Core/Connection.swift +++ b/Sources/SQLite/Core/Connection.swift @@ -429,10 +429,17 @@ public final class Connection { let name = name.quote("'") let savepoint = "SAVEPOINT \(name)" - try transaction(savepoint, block, "RELEASE \(savepoint)", or: "ROLLBACK TO \(savepoint)") + try transaction( + savepoint, + block, + "RELEASE \(savepoint)", + or: "ROLLBACK TO \(savepoint)", + followedBy: "RELEASE \(savepoint)" + ) } - fileprivate func transaction(_ begin: String, _ block: () throws -> Void, _ commit: String, or rollback: String) throws { + fileprivate func transaction(_ begin: String, _ block: () throws -> Void, _ commit: String, + or rollback: String, followedBy cleanup: String? = nil) throws { return try sync { try self.run(begin) do { @@ -440,6 +447,9 @@ public final class Connection { try self.run(commit) } catch { try self.run(rollback) + if let cleanup { + try self.run(cleanup) + } throw error } } diff --git a/Tests/SQLiteTests/Core/ConnectionTests.swift b/Tests/SQLiteTests/Core/ConnectionTests.swift index 9b3534fd..757ced87 100644 --- a/Tests/SQLiteTests/Core/ConnectionTests.swift +++ b/Tests/SQLiteTests/Core/ConnectionTests.swift @@ -315,8 +315,27 @@ class ConnectionTests: SQLiteTestCase { assertSQL("INSERT INTO users (email) VALUES ('alice@example.com')", 2) assertSQL("ROLLBACK TO SAVEPOINT '2'") assertSQL("ROLLBACK TO SAVEPOINT '1'") - assertSQL("RELEASE SAVEPOINT '2'", 0) - assertSQL("RELEASE SAVEPOINT '1'", 0) + assertSQL("RELEASE SAVEPOINT '2'") + assertSQL("RELEASE SAVEPOINT '1'") + } + + func test_savepoint_releasesAfterRollback() throws { + let rollbackError = NSError(domain: "com.stephencelis.SQLiteTests", code: 1, userInfo: nil) + + XCTAssertThrowsError(try db.savepoint("1") { + try db.run("INSERT INTO users (email) VALUES (?)", "alice@example.com") + throw rollbackError + }) { error in + let error = error as NSError + XCTAssertEqual(rollbackError.domain, error.domain) + XCTAssertEqual(rollbackError.code, error.code) + } + + XCTAssertEqual(0, try db.scalar(users.count)) + try db.transaction { + try db.run("INSERT INTO users (email) VALUES (?)", "alice@example.com") + } + XCTAssertEqual(1, try db.scalar(users.count)) } func test_updateHook_setsUpdateHook_withInsert() throws {