Skip to content

fix(plugins)!: SQL Server tables on connect, cell-edit datetime, chip parity; PluginKit ABI 10→11#1183

Merged
datlechin merged 4 commits into
mainfrom
fix/post-connect-schema-load-race
May 10, 2026
Merged

fix(plugins)!: SQL Server tables on connect, cell-edit datetime, chip parity; PluginKit ABI 10→11#1183
datlechin merged 4 commits into
mainfrom
fix/post-connect-schema-load-race

Conversation

@datlechin
Copy link
Copy Markdown
Member

@datlechin datlechin commented May 10, 2026

Summary

User reports two SQL Server bugs after a fresh install:

  1. Sidebar shows "No Tables" until they manually pick a dbo schema, even when the connection form's Database is set.
  2. Editing a cell fails with Conversion failed when converting date and/or time from character string.

Five interlocking root causes:

  1. Post-connect schema-load race (closes feat: Query history #1 for every engine, not just MSSQL). TabRouter.openConnection opens the window before ensureConnected, so the window's first loadSchemaIfNeeded runs against a nil driver and returns silently. The retry hook on databaseDidConnect only re-ran setupPluginDriver, never loadSchemaIfNeeded. The listener now runs both, then tears itself down.
  2. MSSQL queries server truth for current schema: connect() runs SELECT SCHEMA_NAME() after dbuse(...) and seeds _currentSchema from the server's default_schema_name. The connection form's Schema field still acts as an explicit override.
  3. Toolbar chip is grouping-aware: split the overloaded databaseName field into currentDatabase + currentSchema + databaseGroupingStrategy with a computed chipText. SQL Server / PostgreSQL / Oracle / BigQuery (.bySchema engines) show the active schema; MySQL / SQLite (.byDatabase) show the database. Tooltips dispatch on grouping.
  4. switchDatabase mis-routing removed: DatabaseManager.switchDatabase had a branch that, for any driver with supportsSchemaSwitching: true, routed switchDatabase(database) through schemaDriver.switchSchema(database). That treated the database name as a schema name and poisoned lastSchema in UserDefaults. Branch removed; switchDatabase always calls the driver's real switchDatabase. .bySchema engines reset currentSchema to defaultSchemaName after the switch. Oracle gets a one-line switchDatabase override aliasing to switchSchema to preserve its existing behavior.
  5. PluginKit ABI bump 10→11; MSSQL UPDATE/DELETE use PK; datetime ISO 8601 (closes [WIP] Improve system keyboard shortcut functionality #2):
    • PluginDatabaseDriver.generateStatements gains primaryKeyColumns: [String]. All 21 plugin Info.plist files bumped, app currentPluginKitVersion bumped, all driver overrides updated.
    • MSSQL plugin uses PK-only WHERE when known (drops the TOP (1) guard since WHERE is now unique). Falls back to all-columns + TOP (1) when no PK.
    • MSSQLDatetimeFormatter reformats DATETIME / DATETIME2 / SMALLDATETIME values from FreeTDS's msdblib MMM d yyyy h:mm:ss:fffffffAM output to ISO 8601 (yyyy-MM-dd HH:mm:ss[.fffffff]). A Scanner-based parser preserves all fractional digits without Foundation.Date precision loss. Validates AM/PM hours to (1...12), 24-hour to (0...23), year to (1...9999), passes through already-ISO inputs verbatim. SYBMSDATETIMEOFFSET (43) is intentionally excluded until the offset suffix format is verified end-to-end.

Breaking change (! in title)

TableProPluginKitVersion advances 10→11 because generateStatements gained a parameter. All separately-distributed plugins must ship a fresh build with the bumped Info.plist. The user-installed plugin loader rejects mismatched versions to avoid the documented EXC_BAD_INSTRUCTION crash on protocol-witness dispatch.

After merge:

  • App tag v<next> ships Layers 1, 3, 4, and the app-side parts of 5A
  • plugin-mssql-v<next> ships Layers 2, 5A signature, 5B, 5C
  • plugin-oracle-v<next> ships the new switchDatabase override + ABI bump
  • Each remaining separately-distributed plugin (MongoDB, DuckDB, Cassandra, Etcd, CloudflareD1, DynamoDB, BigQuery, LibSQL) needs a plugin-<name>-v<next> tag carrying the signature update + Info.plist bump

Files

  • App: MainContentCoordinator.swift, DatabaseManager+Sessions.swift, ConnectionToolbarState.swift, ConnectionStatusView.swift, TableProToolbarView.swift, MainContentCoordinator+Navigation.swift, MainContentView+Helpers.swift, QueryExecutionCoordinator+Helpers.swift, SessionStateFactory.swift, DataChangeManager.swift, PluginDriverAdapter.swift, PluginManager.swift
  • Plugin protocol: Plugins/TableProPluginKit/PluginDatabaseDriver.swift
  • Plugins (signature update only): MongoDB, ClickHouse, Etcd, DynamoDB, BigQuery, Redis
  • Plugin (full fix): Plugins/MSSQLDriverPlugin/MSSQLPlugin.swift
  • Plugin (switchDatabase override): Plugins/OracleDriverPlugin/OraclePlugin.swift
  • All 21 plugin Info.plist files: TableProPluginKitVersion: 10 → 11
  • Tests: TableProTests/Models/ConnectionToolbarStateTests.swift (chip text per grouping strategy), TableProTests/Plugins/MSSQLDatetimeFormatterTests.swift (parser, including AM/PM 12-hour boundaries and ISO passthrough), TableProTests/Plugins/MSSQLPluginDriverDMLTests.swift (PK-aware UPDATE/DELETE generation, composite keys, NULL PK, identifier escaping)
  • CHANGELOG.md: four user-facing entries under Unreleased > Fixed

Test plan

  • xcodebuild -project TablePro.xcodeproj -scheme TablePro -configuration Debug build -skipPackagePluginValidation
  • xcodebuild ... test -only-testing:TableProTests/ConnectionToolbarStateTests
  • xcodebuild ... test -only-testing:TableProTests/MSSQLDatetimeFormatter
  • xcodebuild ... test -only-testing:TableProTests/MSSQLPluginDriverDML
  • Local Docker SQL Server (port 11433, SA / TableProRepro!2026, seeded Sales and Northwind): delete the existing test connection in Welcome, re-create with Database = Sales, connect → expect Invoices and Regions immediately, chip shows dbo
  • Edit Orders.CustomerId, Cmd+S → expect save success, preview SQL is UPDATE [Orders] SET [CustomerId] = ? WHERE [Id] = ?
  • Edit Orders.PlacedAt to a new datetime, Cmd+S → expect save success, no "Conversion failed" error
  • Connect to local Postgres → chip shows public
  • Connect to local MySQL or SQLite → chip shows database name
  • Reconnect after disconnect → tables still load on first frame, no manual schema pick

Out of scope

  • Removing the connection form's Schema field for MSSQL (kept as user override per design choice)
  • Two-chip UI (database + schema as separate pills); single chip with grouping-aware text was the chosen design
  • Restructuring TabRouter.openConnection to await connect before opening the window; the listener-based fix is the Apple-correct pattern
  • DATETIMEOFFSET round-trip (parser scope-narrowed; left for a follow-up that can verify FreeTDS's offset emit format end-to-end)
  • Datetime ISO conversion for non-MSSQL drivers (Postgres returns ISO via libpq already; other drivers have their own paths)

@datlechin datlechin merged commit 25364b7 into main May 10, 2026
2 checks passed
@datlechin datlechin deleted the fix/post-connect-schema-load-race branch May 10, 2026 10:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant