Skip to content

Commit 8d36abd

Browse files
committed
release: v0.14.0
1 parent f5b5948 commit 8d36abd

7 files changed

Lines changed: 117 additions & 80 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.14.0] - 2026-03-05
11+
1012
### Added
1113

1214
- Microsoft SQL Server (MSSQL) database support via FreeTDS
@@ -665,7 +667,8 @@ TablePro is a native macOS database client built with SwiftUI and AppKit, design
665667
- Custom SQL query templates
666668
- Performance optimized for large datasets
667669

668-
[Unreleased]: https://github.com/datlechin/tablepro/compare/v0.13.0...HEAD
670+
[Unreleased]: https://github.com/datlechin/tablepro/compare/v0.14.0...HEAD
671+
[0.14.0]: https://github.com/datlechin/tablepro/compare/v0.13.0...v0.14.0
669672
[0.13.0]: https://github.com/datlechin/tablepro/compare/v0.12.0...v0.13.0
670673
[0.12.0]: https://github.com/datlechin/tablepro/compare/v0.11.1...v0.12.0
671674
[0.11.1]: https://github.com/datlechin/tablepro/compare/v0.11.0...v0.11.1

TablePro.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@
381381
CODE_SIGN_IDENTITY = "Apple Development";
382382
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
383383
CODE_SIGN_STYLE = Automatic;
384-
CURRENT_PROJECT_VERSION = 25;
384+
CURRENT_PROJECT_VERSION = 26;
385385
DEAD_CODE_STRIPPING = YES;
386386
DEVELOPMENT_TEAM = D7HJ5TFYCU;
387387
ENABLE_APP_SANDBOX = NO;
@@ -415,7 +415,7 @@
415415
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
416416
LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/Libs";
417417
MACOSX_DEPLOYMENT_TARGET = 14.0;
418-
MARKETING_VERSION = 0.13.0;
418+
MARKETING_VERSION = 0.14.0;
419419
OTHER_LDFLAGS = (
420420
"-force_load",
421421
"$(PROJECT_DIR)/Libs/libmariadb.a",
@@ -475,7 +475,7 @@
475475
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
476476
CODE_SIGN_STYLE = Automatic;
477477
COPY_PHASE_STRIP = YES;
478-
CURRENT_PROJECT_VERSION = 25;
478+
CURRENT_PROJECT_VERSION = 26;
479479
DEAD_CODE_STRIPPING = YES;
480480
DEPLOYMENT_POSTPROCESSING = YES;
481481
DEVELOPMENT_TEAM = D7HJ5TFYCU;
@@ -510,7 +510,7 @@
510510
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
511511
LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/Libs";
512512
MACOSX_DEPLOYMENT_TARGET = 14.0;
513-
MARKETING_VERSION = 0.13.0;
513+
MARKETING_VERSION = 0.14.0;
514514
OTHER_LDFLAGS = (
515515
"-force_load",
516516
"$(PROJECT_DIR)/Libs/libmariadb.a",

TablePro/Core/Database/SQLiteDriver.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ final class SQLiteDriver: DatabaseDriver {
275275

276276
/// Synchronous helper to update the interrupt handle under the lock,
277277
/// keeping NSLock usage off the async context.
278-
private nonisolated func setInterruptHandle(_ handle: OpaquePointer?) {
278+
nonisolated private func setInterruptHandle(_ handle: OpaquePointer?) {
279279
interruptLock.lock()
280280
_dbHandleForInterrupt = handle
281281
interruptLock.unlock()
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//
2+
// MainContentCoordinator+Discard.swift
3+
// TablePro
4+
//
5+
// Sidebar transaction execution and discard handling.
6+
//
7+
8+
import Foundation
9+
10+
extension MainContentCoordinator {
11+
// MARK: - Table Creation
12+
13+
/// Execute sidebar changes immediately (single transaction)
14+
func executeSidebarChanges(statements: [String]) async throws {
15+
guard let driver = DatabaseManager.shared.driver(for: connectionId) else {
16+
throw DatabaseError.notConnected
17+
}
18+
19+
let dbType = connection.type
20+
var allStatements: [String] = []
21+
22+
// Add database-specific BEGIN / START TRANSACTION
23+
let beginStatement: String
24+
switch dbType {
25+
case .mysql, .mariadb:
26+
beginStatement = "START TRANSACTION"
27+
case .mssql:
28+
beginStatement = "BEGIN TRANSACTION"
29+
default:
30+
beginStatement = "BEGIN"
31+
}
32+
allStatements.append(beginStatement)
33+
34+
// Add user statements
35+
allStatements.append(contentsOf: statements)
36+
37+
// Add COMMIT
38+
allStatements.append("COMMIT")
39+
40+
// Execute all statements sequentially
41+
do {
42+
for sql in allStatements {
43+
_ = try await driver.execute(query: sql)
44+
}
45+
} catch {
46+
// Try to rollback on error
47+
_ = try? await driver.execute(query: "ROLLBACK")
48+
throw error
49+
}
50+
}
51+
52+
// MARK: - Discard Handling
53+
54+
func handleDiscard(
55+
pendingTruncates: inout Set<String>,
56+
pendingDeletes: inout Set<String>
57+
) {
58+
let originalValues = changeManager.getOriginalValues()
59+
if let index = tabManager.selectedTabIndex {
60+
for (rowIndex, columnIndex, originalValue) in originalValues {
61+
if rowIndex < tabManager.tabs[index].resultRows.count {
62+
tabManager.tabs[index].resultRows[rowIndex].values[columnIndex] = originalValue
63+
}
64+
}
65+
66+
let insertedIndices = changeManager.insertedRowIndices.sorted(by: >)
67+
for rowIndex in insertedIndices {
68+
if rowIndex < tabManager.tabs[index].resultRows.count {
69+
tabManager.tabs[index].resultRows.remove(at: rowIndex)
70+
}
71+
}
72+
}
73+
74+
pendingTruncates.removeAll()
75+
pendingDeletes.removeAll()
76+
changeManager.clearChanges()
77+
78+
if let index = tabManager.selectedTabIndex {
79+
tabManager.tabs[index].pendingChanges = TabPendingChanges()
80+
}
81+
82+
NotificationCenter.default.post(name: .databaseDidConnect, object: nil)
83+
}
84+
}

TablePro/Views/Main/MainContentCoordinator.swift

Lines changed: 0 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,80 +1166,6 @@ final class MainContentCoordinator {
11661166
}
11671167
}
11681168

1169-
// MARK: - Table Creation
1170-
1171-
/// Execute sidebar changes immediately (single transaction)
1172-
func executeSidebarChanges(statements: [String]) async throws {
1173-
guard let driver = DatabaseManager.shared.driver(for: connectionId) else {
1174-
throw DatabaseError.notConnected
1175-
}
1176-
1177-
let dbType = connection.type
1178-
var allStatements: [String] = []
1179-
1180-
// Add database-specific BEGIN / START TRANSACTION
1181-
let beginStatement: String
1182-
switch dbType {
1183-
case .mysql, .mariadb:
1184-
beginStatement = "START TRANSACTION"
1185-
case .mssql:
1186-
beginStatement = "BEGIN TRANSACTION"
1187-
default:
1188-
beginStatement = "BEGIN"
1189-
}
1190-
allStatements.append(beginStatement)
1191-
1192-
// Add user statements
1193-
allStatements.append(contentsOf: statements)
1194-
1195-
// Add COMMIT
1196-
allStatements.append("COMMIT")
1197-
1198-
// Execute all statements sequentially
1199-
do {
1200-
for sql in allStatements {
1201-
_ = try await driver.execute(query: sql)
1202-
}
1203-
} catch {
1204-
// Try to rollback on error
1205-
_ = try? await driver.execute(query: "ROLLBACK")
1206-
throw error
1207-
}
1208-
}
1209-
1210-
// MARK: - Discard Handling
1211-
1212-
func handleDiscard(
1213-
pendingTruncates: inout Set<String>,
1214-
pendingDeletes: inout Set<String>
1215-
) {
1216-
let originalValues = changeManager.getOriginalValues()
1217-
if let index = tabManager.selectedTabIndex {
1218-
for (rowIndex, columnIndex, originalValue) in originalValues {
1219-
if rowIndex < tabManager.tabs[index].resultRows.count {
1220-
tabManager.tabs[index].resultRows[rowIndex].values[columnIndex] = originalValue
1221-
}
1222-
}
1223-
1224-
let insertedIndices = changeManager.insertedRowIndices.sorted(by: >)
1225-
for rowIndex in insertedIndices {
1226-
if rowIndex < tabManager.tabs[index].resultRows.count {
1227-
tabManager.tabs[index].resultRows.remove(at: rowIndex)
1228-
}
1229-
}
1230-
}
1231-
1232-
pendingTruncates.removeAll()
1233-
pendingDeletes.removeAll()
1234-
changeManager.clearChanges()
1235-
1236-
if let index = tabManager.selectedTabIndex {
1237-
tabManager.tabs[index].pendingChanges = TabPendingChanges()
1238-
}
1239-
1240-
NotificationCenter.default.post(name: .databaseDidConnect, object: nil)
1241-
}
1242-
12431169
/// Remove shared schema provider when a connection disconnects
12441170
static func clearSharedSchema(for connectionId: UUID) {
12451171
sharedSchemaProviders.removeValue(forKey: connectionId)

docs/changelog.mdx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,18 @@ rss: true
88
### New Features
99

1010
- **Microsoft SQL Server Support**: Connect to SQL Server 2017+ databases via FreeTDS with schema browsing, table structure, indexes, foreign keys, and paginated queries
11+
- **Edit and Delete Without Primary Key**: Edit and delete rows in tables that don't have a primary key
12+
13+
### Bug Fixes
14+
15+
- Fixed MSSQL connection losing selected database after disconnect and reconnect when no default database is configured
16+
- Fixed DELETE operations on tables without a primary key being silently dropped when row data is missing
17+
- Fixed high CPU and RAM usage on app launch from blocking storage init, unsynchronized health monitors, and excessive retry loops
18+
- Fixed slow database switcher loading by replacing N+1 metadata queries with single batched queries
19+
- Fixed slow Redis key browsing by pipelining TYPE and TTL commands in a single round trip
20+
- Fixed slow SQL export startup by batching COUNT(*) queries and dependent lookups
21+
- Fixed slow AI Chat schema loading by fetching all foreign keys in a single bulk query
22+
- Fixed O(n) string operations causing high CPU in geometry parsing, Redis driver, and autocomplete scoring
1123
</Update>
1224

1325
<Update label="March 4, 2026" description="v0.13.0">

docs/vi/changelog.mdx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,18 @@ rss: true
88
### Tính năng mới
99

1010
- **Hỗ trợ Microsoft SQL Server**: Kết nối đến cơ sở dữ liệu SQL Server 2017+ qua FreeTDS với duyệt schema, cấu trúc bảng, chỉ mục, khóa ngoại và truy vấn phân trang
11+
- **Sửa và xóa không cần khóa chính**: Sửa và xóa hàng trong bảng không có khóa chính
12+
13+
### Sửa lỗi
14+
15+
- Sửa lỗi kết nối MSSQL mất cơ sở dữ liệu đã chọn sau khi ngắt kết nối và kết nối lại khi không có cơ sở dữ liệu mặc định
16+
- Sửa lỗi thao tác DELETE trên bảng không có khóa chính bị bỏ qua khi thiếu dữ liệu hàng
17+
- Sửa lỗi CPU và RAM cao khi khởi động ứng dụng do khởi tạo storage bị chặn, health monitor không đồng bộ và vòng lặp thử lại quá nhiều
18+
- Sửa lỗi tải chậm trình chuyển đổi cơ sở dữ liệu bằng cách thay thế N+1 truy vấn metadata bằng truy vấn gộp
19+
- Sửa lỗi duyệt key Redis chậm bằng cách gộp lệnh TYPE và TTL trong một lượt gửi
20+
- Sửa lỗi khởi động xuất SQL chậm bằng cách gộp truy vấn COUNT(*) và các truy vấn phụ thuộc
21+
- Sửa lỗi tải schema AI Chat chậm bằng cách lấy tất cả khóa ngoại trong một truy vấn gộp
22+
- Sửa lỗi thao tác chuỗi O(n) gây CPU cao trong phân tích geometry, Redis driver và chấm điểm autocomplete
1123
</Update>
1224

1325
<Update label="4 tháng 3, 2026" description="v0.13.0">

0 commit comments

Comments
 (0)