From a8b31a913f6d8f8a7e6c32ad5aa05c2aeb86992f Mon Sep 17 00:00:00 2001 From: ShiroKSH Date: Sat, 11 Jul 2026 17:13:06 +0300 Subject: [PATCH 1/3] fix: release rolled-back savepoints --- Sources/SQLite/Core/Connection.swift | 14 ++++++++++-- Tests/SQLiteTests/Core/ConnectionTests.swift | 23 ++++++++++++++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) 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 { From ed1bd70c8ef84866a167028220fba32f0a5ecc33 Mon Sep 17 00:00:00 2001 From: ShiroKSH Date: Sat, 8 Aug 2026 23:09:02 +0300 Subject: [PATCH 2/3] style: keep connection within lint limit --- Sources/SQLite/Core/Connection.swift | 38 +++++++++++++++------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift index d202c564..625523c7 100644 --- a/Sources/SQLite/Core/Connection.swift +++ b/Sources/SQLite/Core/Connection.swift @@ -438,23 +438,6 @@ public final class Connection { ) } - 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 { - try block() - try self.run(commit) - } catch { - try self.run(rollback) - if let cleanup { - try self.run(cleanup) - } - throw error - } - } - } - /// Interrupts any long-running queries. public func interrupt() { sqlite3_interrupt(handle) @@ -774,6 +757,27 @@ public final class Connection { } +extension Connection { + + 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 { + try block() + try self.run(commit) + } catch { + try self.run(rollback) + if let cleanup { + try self.run(cleanup) + } + throw error + } + } + } + +} + extension Connection: CustomStringConvertible { public var description: String { From ac903cc90dd48da65eb9296c1d135770f849a6ae Mon Sep 17 00:00:00 2001 From: ShiroKSH Date: Sun, 9 Aug 2026 12:57:08 +0300 Subject: [PATCH 3/3] ci: resolve Tuist package before installation --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 838af7f7..b7ac520c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -75,6 +75,7 @@ jobs: brew update brew install tuist cd Tests/Tuist/SQLite-Test + swift package --package-path Tuist resolve tuist install # tuist test build-linux: