Skip to content

Commit 8b220f1

Browse files
authored
Merge pull request #594 from TableProApp/fix/sqlite-schema-versioning
fix: add SQLite schema versioning for future migrations
2 parents 138e6cc + 451f2e0 commit 8b220f1

File tree

3 files changed

+69
-0
lines changed

3 files changed

+69
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1717
- Fix crash when closing window during SSH tunnel connection (use-after-free in libssh2)
1818
- Fix potential deadlock in SSH host key verification prompts (semaphore → async/await)
1919
- Fix data race in ConnectionStorage, GroupStorage, and TagStorage (added @MainActor isolation)
20+
- Add schema versioning to SQLite databases (query history, favorites) for future migrations
2021

2122
### Added
2223

TablePro/Core/Storage/QueryHistoryStorage.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,42 @@ final class QueryHistoryStorage {
150150
execute("PRAGMA synchronous=NORMAL;")
151151

152152
createTables()
153+
migrateIfNeeded()
153154
}
154155

156+
// MARK: - Schema Migration
157+
158+
private func migrateIfNeeded() {
159+
let currentVersion = getUserVersion()
160+
161+
// Future migrations go here:
162+
// if currentVersion < 2 {
163+
// execute("ALTER TABLE history ADD COLUMN new_col TEXT;")
164+
// }
165+
166+
let targetVersion: Int32 = 1
167+
if currentVersion < targetVersion {
168+
setUserVersion(targetVersion)
169+
}
170+
}
171+
172+
private func getUserVersion() -> Int32 {
173+
var statement: OpaquePointer?
174+
defer { sqlite3_finalize(statement) }
175+
guard sqlite3_prepare_v2(db, "PRAGMA user_version", -1, &statement, nil) == SQLITE_OK,
176+
sqlite3_step(statement) == SQLITE_ROW
177+
else {
178+
return 0
179+
}
180+
return sqlite3_column_int(statement, 0)
181+
}
182+
183+
private func setUserVersion(_ version: Int32) {
184+
execute("PRAGMA user_version = \(version);")
185+
}
186+
187+
// MARK: - Table Creation
188+
155189
private func createTables() {
156190
// History table
157191
let historyTable = """

TablePro/Core/Storage/SQLFavoriteStorage.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,42 @@ internal final class SQLFavoriteStorage {
109109
execute("PRAGMA synchronous=NORMAL;")
110110

111111
createTables()
112+
migrateIfNeeded()
112113
}
113114

115+
// MARK: - Schema Migration
116+
117+
private func migrateIfNeeded() {
118+
let currentVersion = getUserVersion()
119+
120+
// Future migrations go here:
121+
// if currentVersion < 2 {
122+
// execute("ALTER TABLE favorites ADD COLUMN new_col TEXT;")
123+
// }
124+
125+
let targetVersion: Int32 = 1
126+
if currentVersion < targetVersion {
127+
setUserVersion(targetVersion)
128+
}
129+
}
130+
131+
private func getUserVersion() -> Int32 {
132+
var statement: OpaquePointer?
133+
defer { sqlite3_finalize(statement) }
134+
guard sqlite3_prepare_v2(db, "PRAGMA user_version", -1, &statement, nil) == SQLITE_OK,
135+
sqlite3_step(statement) == SQLITE_ROW
136+
else {
137+
return 0
138+
}
139+
return sqlite3_column_int(statement, 0)
140+
}
141+
142+
private func setUserVersion(_ version: Int32) {
143+
execute("PRAGMA user_version = \(version);")
144+
}
145+
146+
// MARK: - Table Creation
147+
114148
private func createTables() {
115149
let favoritesTable = """
116150
CREATE TABLE IF NOT EXISTS favorites (

0 commit comments

Comments
 (0)