Skip to content

Commit 3489aa1

Browse files
authored
Add VACUUM support (#19)
1 parent 5a29af8 commit 3489aa1

2 files changed

Lines changed: 150 additions & 0 deletions

File tree

Sources/SQLite/Database.swift

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,54 @@ extension Database {
238238
}
239239
}
240240

241+
extension Database {
242+
public enum AutoVacuumMode: Int {
243+
case none = 0
244+
case incremental = 2
245+
case full = 1
246+
247+
fileprivate init?(_ rawValue: Int?) {
248+
switch rawValue {
249+
case .some(0): self = .none
250+
case .some(2): self = .incremental
251+
case .some(1): self = .full
252+
default: return nil
253+
}
254+
}
255+
}
256+
257+
public var autoVacuumMode: AutoVacuumMode {
258+
get {
259+
do {
260+
guard let result = try execute(raw: "PRAGMA auto_vacuum;").first
261+
else { return .none }
262+
return AutoVacuumMode(result["auto_vacuum"]?.intValue) ?? .none
263+
} catch {
264+
assertionFailure("Could not get auto_vacuum: \(error))")
265+
return .none
266+
}
267+
}
268+
set {
269+
do { try execute(raw: "PRAGMA auto_vacuum = \(newValue.rawValue);") }
270+
catch { assertionFailure("Could not set auto_vacuum to \(newValue): \(error)") }
271+
}
272+
}
273+
274+
public func incrementalVacuum(_ pages: Int? = nil) throws {
275+
let sql: SQL
276+
if let pages = pages {
277+
sql = "PRAGMA incremental_vacuum(\(pages));"
278+
} else {
279+
sql = "PRAGMA incremental_vacuum;"
280+
}
281+
try execute(raw: sql)
282+
}
283+
284+
public func vacuum() throws {
285+
try execute(raw: "VACUUM;")
286+
}
287+
}
288+
241289
extension Database {
242290
func createUpdateHandler(_ block: @escaping (String) -> Void) {
243291
let updateBlock: UpdateHookCallback = { _, _, _, tableName, _ in

Tests/SQLiteTests/DatabaseTests.swift

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,108 @@ class DatabaseTests: XCTestCase {
3838
XCTAssertTrue(database.supportsJSON)
3939
}
4040

41+
func testAutoVacuumMode() throws {
42+
XCTAssertEqual(.none, database.autoVacuumMode)
43+
44+
database.autoVacuumMode = .full
45+
XCTAssertEqual(.full, database.autoVacuumMode)
46+
47+
database.autoVacuumMode = .incremental
48+
XCTAssertEqual(.incremental, database.autoVacuumMode)
49+
}
50+
51+
func testIncrementalVacuumDoesNotThrowIfModeIsNotIncremental() throws {
52+
XCTAssertEqual(.none, database.autoVacuumMode)
53+
try database.incrementalVacuum()
54+
}
55+
56+
func testIncrementalVacuum() throws {
57+
func getPageCount() throws -> Int {
58+
let result = try database.execute(raw: "PRAGMA page_count;").first
59+
return try XCTUnwrap(result?["page_count"]?.intValue)
60+
}
61+
62+
database.autoVacuumMode = .incremental
63+
64+
XCTAssertEqual(1, try getPageCount())
65+
66+
try database.execute(raw: _createTableWithBlob)
67+
68+
try database.inTransaction { (db) -> Void in
69+
try (0..<1000).forEach { (index) in
70+
let args: SQLiteArguments = [
71+
"id": .integer(Int64(index)), "data": .data(_textData)
72+
]
73+
try db.write(_insertIDAndData, arguments: args)
74+
}
75+
}
76+
77+
XCTAssertGreaterThan(try getPageCount(), 3)
78+
79+
try database.write("DELETE FROM test;", arguments: [:])
80+
try database.incrementalVacuum()
81+
82+
XCTAssertEqual(3, try getPageCount())
83+
}
84+
85+
func testIncrementalVacuumWithPageCount() throws {
86+
func getPageCount() throws -> Int {
87+
let result = try database.execute(raw: "PRAGMA page_count;").first
88+
return try XCTUnwrap(result?["page_count"]?.intValue)
89+
}
90+
91+
database.autoVacuumMode = .incremental
92+
93+
XCTAssertEqual(1, try getPageCount())
94+
95+
try database.execute(raw: _createTableWithBlob)
96+
97+
try database.inTransaction { (db) -> Void in
98+
try (0..<1000).forEach { (index) in
99+
let args: SQLiteArguments = [
100+
"id": .integer(Int64(index)), "data": .data(_textData)
101+
]
102+
try db.write(_insertIDAndData, arguments: args)
103+
}
104+
}
105+
106+
let pageCount = try getPageCount()
107+
XCTAssertGreaterThan(pageCount, 3)
108+
109+
try database.write("DELETE FROM test;", arguments: [:])
110+
try database.incrementalVacuum(2)
111+
112+
XCTAssertEqual(pageCount - 2, try getPageCount())
113+
}
114+
115+
func testVacuum() throws {
116+
func getPageCount() throws -> Int {
117+
let result = try database.execute(raw: "PRAGMA page_count;").first
118+
return try XCTUnwrap(result?["page_count"]?.intValue)
119+
}
120+
121+
XCTAssertEqual(.none, database.autoVacuumMode)
122+
XCTAssertEqual(0, try getPageCount())
123+
124+
try database.execute(raw: _createTableWithBlob)
125+
126+
try database.inTransaction { (db) -> Void in
127+
try (0..<1000).forEach { (index) in
128+
let args: SQLiteArguments = [
129+
"id": .integer(Int64(index)), "data": .data(_textData)
130+
]
131+
try db.write(_insertIDAndData, arguments: args)
132+
}
133+
}
134+
135+
XCTAssertGreaterThan(try getPageCount(), 2)
136+
137+
try database.write("DELETE FROM test;", arguments: [:])
138+
try database.vacuum()
139+
140+
XCTAssertEqual(2, try getPageCount())
141+
}
142+
41143
func testCreateTable() throws {
42144
XCTAssertNoThrow(try database.execute(raw: _createTableWithBlob))
43145
let tableNames = try database.tables()

0 commit comments

Comments
 (0)