Skip to content

Commit 935a8b6

Browse files
authored
fix: stop prompting "MariaDB plugin not installed" for lazy built-in drivers (#1053)
1 parent 25efaa9 commit 935a8b6

5 files changed

Lines changed: 7 additions & 9 deletions

File tree

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

1818
### Fixed
1919

20+
- New connection chooser falsely prompted "The MariaDB plugin is not installed" before the lazy MySQL bundle was activated. `PluginManager` registers built-in drivers (MySQL, PostgreSQL, SQLite, ClickHouse, Redis) lazily and only populates `driverPlugins[typeId]` when a connection actually loads the bundle, but `isDriverLoaded(for:)` only consulted that loaded map and returned `false` for every unactivated driver. Picking MariaDB in the type chooser (which resolves to the MySQL plugin via `reverseTypeIndex`) routed through the install-from-marketplace alert instead of opening the connection form. The check is now `isDriverInstalled(for:)` and also reads `lazyDriverURLs[typeId]`, which `registerLazyPluginEntry` populates for every type ID in the manifest, including aliases. Same fix removes the spurious "Not Installed" badge from the type chooser grid. The unused `isDriverAvailable(for:)` (identical body, zero callers) is gone.
2021
- SQL Server and Oracle connections silently ignored schema selection from the Cmd+K Quick Switcher. Picking a schema like `rpt` (or any non-default) appeared in the search list but did nothing on selection. Three things were misaligned: `QuickSwitcherViewModel` listed both engines in a hardcoded schema-fetch allowlist (so schemas appeared as searchable items), but `PluginMetadataRegistry` had `supportsSchemaSwitching: false` for both, and `MainContentCoordinator+Navigation.switchSchema` early-returned silently on that flag with no log and no user feedback. The MSSQL and Oracle plugin drivers themselves already implemented `switchSchema(to:)` correctly (MSSQL updates `_currentSchema` for table-listing filters; Oracle runs `ALTER SESSION SET CURRENT_SCHEMA`), so flipping the registry flag is sufficient. `supportsSchemaSwitching` is now `true` for both, post-connect actions include `selectSchemaFromLastSession` so a chosen schema persists across reconnects, the `QuickSwitcherViewModel` allowlist is replaced with `PluginManager.shared.supportsSchemaSwitching(for:)` so future engines auto-pick up the right behavior, and the early-return in `switchSchema` now logs and surfaces a `Schema Switching Not Supported` alert instead of failing silently. Reported in r/macapps feedback.
2122
- iOS: app crashed with `EXC_BREAKPOINT` "Not enough bits to represent the passed value" when opening some MySQL tables (TestFlight report on a 100k-record table). `MySQLActor.execute` did `Int(mysql_affected_rows(mysql))`, but libmariadb is documented to return `~(my_ulonglong)0` (= `UInt64.max`) as an error sentinel ("for a SELECT, mysql_affected_rows() was called prior to mysql_store_result()") and the unchecked Int conversion trapped on the sentinel. The same shape applied to per-cell `mysql_fetch_lengths` values, which on arm64 are `unsigned long` (`UInt64`); a length above `Int.max` would trap rather than fail the read recoverably. Both paths now use `Int(clamping:)` and the affected-rows sites explicitly map the `~0` sentinel to `0`. Same hardening applied to the macOS MySQL plugin's two cell-length conversion sites in `MariaDBPluginConnection.swift` (default-fetch and streaming) which had identical exposure but no reported crash.
2223
- iOS: connections to `.local` (Bonjour) hostnames and other local-network addresses (10.x, 192.168.x, 172.16-31.x, 169.254.x, IPv6 ULA / link-local) timed out silently. The bundle was missing `NSLocalNetworkUsageDescription` and `NSBonjourServices`, so iOS never prompted the user for Local Network access and quietly dropped every outbound `connect()` to a local-network address. Most visible variant: SSH Tunnel set to `Some-MacBook.local`, error surfaced as "MySQL connection failed: Lost connection to server at 'handshake: reading initial communication packet', system error: 60" (errno 60 = `ETIMEDOUT`). Both Info.plist keys are now declared (purpose string explains database/SSH access; Bonjour types `_ssh._tcp`, `_mysql._tcp`, `_postgresql._tcp`, `_redis._tcp`). A new `LocalNetworkPermission` actor starts an `NWBrowser` for `_ssh._tcp` the first time a connection targets a local-network host (the documented Apple pattern from the DTS "Local Network Privacy FAQ" since a bare `connect()` does not always trigger the consent prompt for `getaddrinfo`-based connections), watches the `NWBrowser.State` stream for `.ready` (granted) or `.waiting`/`.failed` (unavailable), and caches the resolution per process. On denial the gate throws `LocalNetworkPermissionError.unavailable` immediately on every subsequent attempt instead of waiting for the 10-second TCP timeout, so the error surfaces in under a second. Concurrent first-time gate calls share one in-flight resolution `Task` so a parallel SSH + DB connect does not double-prompt. Wired through `SSHTunnelFactory.create()` and `MySQLDriver` / `PostgreSQLDriver` / `RedisDriver` `connect()` (loopback-only and non-local hosts no-op the gate). `ErrorClassifier` matches the typed `LocalNetworkPermissionError` directly and falls back to detecting `ETIMEDOUT` on SSH-enabled or local-network connections; both paths show "Open Settings > Privacy & Security > Local Network and turn TablePro on, then try again." instead of the previous generic "server is not responding" or misleading SSH-handshake copy.

TablePro/Core/Plugins/PluginManager+Registration.swift

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -188,12 +188,9 @@ extension PluginManager {
188188

189189
// MARK: - Driver Availability
190190

191-
func isDriverAvailable(for databaseType: DatabaseType) -> Bool {
192-
driverPlugins[databaseType.pluginTypeId] != nil
193-
}
194-
195-
func isDriverLoaded(for databaseType: DatabaseType) -> Bool {
196-
driverPlugins[databaseType.pluginTypeId] != nil
191+
func isDriverInstalled(for databaseType: DatabaseType) -> Bool {
192+
let typeId = databaseType.pluginTypeId
193+
return driverPlugins[typeId] != nil || lazyDriverURLs[typeId] != nil
197194
}
198195

199196
func sqlDialect(for databaseType: DatabaseType) -> SQLDialectDescriptor? {

TablePro/Core/Plugins/PluginManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ final class PluginManager {
8484

8585
private var pendingPluginURLs: [(url: URL, source: PluginSource)] = []
8686

87-
@ObservationIgnored private var lazyDriverURLs: [String: URL] = [:]
87+
@ObservationIgnored private(set) var lazyDriverURLs: [String: URL] = [:]
8888
@ObservationIgnored private var lazyExportURLs: [String: URL] = [:]
8989
@ObservationIgnored private var lazyImportURLs: [String: URL] = [:]
9090
@ObservationIgnored private var activatedBundleIds: Set<String> = []

TablePro/Views/Connection/TypeChooser/DatabaseTypeChooserSheet.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,6 @@ private struct DatabaseTypeChooserRow: View {
170170
}
171171

172172
private var shouldShowNotInstalledBadge: Bool {
173-
type.isDownloadablePlugin && !PluginManager.shared.isDriverLoaded(for: type)
173+
type.isDownloadablePlugin && !PluginManager.shared.isDriverInstalled(for: type)
174174
}
175175
}

TablePro/Views/Connection/WelcomeWindowView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ struct WelcomeWindowView: View {
123123
welcomeChooserState = WelcomeChooserState(
124124
initialType: payload.initialType,
125125
onSelected: { type in
126-
if PluginManager.shared.isDriverLoaded(for: type) {
126+
if PluginManager.shared.isDriverInstalled(for: type) {
127127
PendingNewConnectionType.shared.set(type)
128128
payload.onSelected(type)
129129
} else {

0 commit comments

Comments
 (0)