diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ce72f7..7129d66 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,10 +5,11 @@ on: push jobs: test: runs-on: macos-13 - + steps: - uses: actions/checkout@v3 - name: Select Xcode 15 run: sudo xcode-select -s /Applications/Xcode_15.0.app - name: Test run: swift test + diff --git a/Sources/SQLite/SQLiteDatabase.swift b/Sources/SQLite/SQLiteDatabase.swift index 4cb95d5..81679a1 100644 --- a/Sources/SQLite/SQLiteDatabase.swift +++ b/Sources/SQLite/SQLiteDatabase.swift @@ -111,6 +111,20 @@ public final class SQLiteDatabase: DatabaseProtocol, @unchecked Sendable { ) } + public func truncate() throws { + switch database { + case let .pool(pool): + try pool.writeWithoutTransaction { db in + _ = try db.execute(raw: "PRAGMA wal_checkpoint(TRUNCATE);") + } + + case let .queue(queue): + try queue.writeWithoutTransaction { db in + _ = try db.execute(raw: "PRAGMA wal_checkpoint(TRUNCATE);") + } + } + } + // NOTE: This function is only really meant to be called in tests. public func close() throws { changeNotifier.stop() @@ -275,7 +289,7 @@ public extension SQLiteDatabase { @discardableResult func execute(raw sql: SQL) async throws -> [SQLiteRow] { do { - return try await database.writer.write { db in + return try await database.writer.writeWithoutTransaction { db in try db.execute(raw: sql) } } catch { @@ -339,7 +353,7 @@ public extension SQLiteDatabase { @discardableResult func execute(raw sql: SQL) throws -> [SQLiteRow] { do { - return try database.writer.write { db in + return try database.writer.writeWithoutTransaction { db in try db.execute(raw: sql) } } catch { @@ -644,13 +658,10 @@ private extension SQLiteDatabase { config.journalMode = isInMemory ? .default : .wal // NOTE: GRDB recommends `defaultTransactionKind` be set // to `.immediate` in order to prevent `SQLITE_BUSY` - // errors. Using `.immediate` appears to disable - // automatic vacuuming. + // errors. // // https://swiftpackageindex.com/groue/grdb.swift/v6.24.2/documentation/grdb/databasesharing#How-to-limit-the-SQLITEBUSY-error - config.defaultTransactionKind = isInMemory - ? .deferred - : .immediate + config.defaultTransactionKind = .immediate config.busyMode = .timeout(busyTimeout) config.observesSuspensionNotifications = true config.maximumReaderCount = max( @@ -770,7 +781,7 @@ private enum Database { arguments: SQLiteArguments = [:] ) throws { do { - try writer.write { db in + try writer.writeWithoutTransaction { db in try db.write(sql, arguments: arguments) } } catch { diff --git a/Tests/SQLiteTests/SQLiteDatabaseTests.swift b/Tests/SQLiteTests/SQLiteDatabaseTests.swift index bf9c7a4..67b3651 100644 --- a/Tests/SQLiteTests/SQLiteDatabaseTests.swift +++ b/Tests/SQLiteTests/SQLiteDatabaseTests.swift @@ -253,6 +253,27 @@ final class SQLiteDatabaseTests: XCTestCase { } } + func testCheckpointUsingTruncate() throws { + try Sandbox.execute { directory in + let path = directory.appendingPathComponent("test.db").path + let db = try SQLiteDatabase(path: path) + defer { try? db.close() } + + try db.execute(raw: _createTableWithBlob) + + try db.inTransaction { db in + for index in 0 ..< 100 { + let args: SQLiteArguments = [ + "id": .integer(Int64(index)), "data": .data(_textData), + ] + try db.write(_insertIDAndData, arguments: args) + } + } + + try db.truncate() + } + } + func testCreateTable() throws { XCTAssertNoThrow(try database.execute(raw: _createTableWithBlob)) let tableNames = try database.tables()