Skip to content

fix(coordinator): keep a SQL Server table on the schema it was listed under - #2006

Merged
datlechin merged 2 commits into
mainfrom
fix-2004-mssql-custom-schema
Aug 1, 2026
Merged

fix(coordinator): keep a SQL Server table on the schema it was listed under#2006
datlechin merged 2 commits into
mainfrom
fix-2004-mssql-custom-schema

Conversation

@datlechin

Copy link
Copy Markdown
Member

Fixes #2004. Opening a SQL Server table or view outside the login's default schema failed with Invalid object name.

What the error actually says

Zooming the reporter's screenshot shows Invalid object name 'def_en… with the quote against the name, so the statement carried a bare, unqualified object name. The schema was nil or blank when the SQL was built, not merely wrong. SQL Server has no session-level schema, so an unqualified name has nowhere to resolve; Postgres and MySQL hide the same class of bug behind search_path and the default database.

Ruling out the plugin

#1758 taught the MSSQL plugin to qualify browse queries, so the obvious theory was a stale driver. It is not:

  • nm on the published PluginKit-18 binary (v1.0.32, what 0.61.0 resolves to) shows MSSQLSchemaQueries.qualifiedName and browse(schema:). The PluginKit-17 binary (v1.0.25) has neither and is the build that emits SELECT * FROM [table].
  • minimumCompatiblePluginKitVersion = 18 rejects a kit-17 plugin on 0.61.0 and reconciliation replaces it, so a user on this version cannot be silently running the broken build.

Checked against SQL Server 2022 in Docker with the reporter's shape (login default schema dbo, tables only in custom): SELECT SCHEMA_NAME() returns dbo and INFORMATION_SCHEMA.SCHEMATA does list custom, so the schema list is not being hidden by permissions either. The defect is app-side.

Root cause

A table's schema was not part of its identity. It was stamped inconsistently at listing time and then re-guessed from session.currentSchema at query-build time, and that session value could itself be wrong. Four defects compound:

1. A database switch desynced the session from the driver. DatabaseManager.switchDatabase wrote the plugin's static default schema into the session for .bySchema engines and never told the driver. Object listings read the driver's schema and table queries read the session's, so after any database switch the sidebar and the query were on different schemas, and the user's chosen schema was silently discarded.

2. PluginDriverAdapter.fetchTables() claimed every table had no schema. The rows came from pluginDriver.currentSchema, but the no-arg overload stamped schema: nil while fetchTables(schema:) directly below it stamped the real value. The no-arg one backs SchemaService's flat list, which feeds session.tables, favourites, the quick switcher, and autocomplete.

3. The database tree threw away the schema it already had. open(_ ref:) called openTableTab(ref.table, …) and dropped ref.schema. It worked only because mapPluginTable gained a back-fill on 2026-07-29 (#1989), one day before 0.61.0.

4. The tree's corrective schema switch was gated on the wrong value. activate(_:) compared against toolbarState.currentSchema, the window's mirror, which a database switch does not update. Running immediately after defect 1 moved the session schema, the guard skipped the switch exactly when the session needed it.

The fix

Resolve a table's schema once, where the row is listed, and stop treating session.currentSchema as a source of truth:

  • switchDatabase moves the driver to the engine's default schema and then records what the driver is actually using, so session and driver can never disagree. MainContentCoordinator.switchDatabase refreshes the toolbar mirror through the same path.
  • resolvedSchemaName treats a blank schema as absent at both tiers. A blank name is the one value that reaches a qualifier and produces an unqualified [table].
  • fetchTables() delegates to fetchTables(schema:) so both listing paths stamp the schema the rows came from.
  • The tree passes ref.schema explicitly and gates its schema switch on the live session schema.
  • MSSQLPluginDriver.fetchTables populates PluginTableInfo.schema at the source, and every schema fallback in the driver routes through one effectiveSchema helper that also rejects a blank name.

Plugin release

The MSSQL change is additive (call sites only, no new protocol requirement, no transfer-type layout change), so no PluginKit ABI bump. It needs a plugin-mssql-v1.0.33 release to reach users. The app-side fixes stand on their own without it: the adapter back-fills the schema regardless of what the driver reports.

Tests

Ten new tests:

  • Schema precedence: explicit argument beats the listed table's schema, which beats the session's.
  • A blank schema on the table falls back; a blank session schema leaves the tab without one rather than emitting an empty qualifier.
  • A database switch never leaves the session and the driver on different schemas.
  • fetchTables() stamps the schema its rows were read from, and stays schema-less for an engine without schemas.
  • End to end: a table in a non-default schema opens a query containing [custom].[def_encounter].

The existing test asserting switchDatabase writes the plugin default into the session encoded defect 1. It now asserts the driver moved too and that both agree.

Verification

BUILD SUCCEEDED for the app and for the MSSQLDriver scheme (the plugin target is not in the app scheme, so it was built separately). swiftlint lint --strict clean on every changed file; the four violations in the MSSQL plugin files are pre-existing on main and were left alone.

Two things are honestly unverified:

  • No end-to-end reproduction of the reporter's exact trigger. Each defect independently produces a wrong or missing schema and together they close every route I could find to an unqualified name, but I could not get the reporter's specific state to reproduce locally.
  • No clean full-suite baseline. TableProTests fails 74 tests locally, and 74 on main as well, with membership shuffling between runs as a parallel worker crashes and drags whatever it was running down with it. The new suites pass in isolation and alongside the suites they collided with.

Follow-up

Oracle's fetchTables has the same shape and is the other engine with no session-schema fallback. Left out of this PR deliberately.

@mintlify

mintlify Bot commented Aug 1, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟢 Ready View Preview Aug 1, 2026, 5:21 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Signed-off-by: Ngô Quốc Đạt <datlechin@gmail.com>
@datlechin
datlechin merged commit b2f4125 into main Aug 1, 2026
2 of 3 checks passed
@datlechin
datlechin deleted the fix-2004-mssql-custom-schema branch August 1, 2026 06:38
pull Bot pushed a commit to CrazyForks/TablePro that referenced this pull request Aug 1, 2026
… under (TableProApp#2004) (TableProApp#2006)

Signed-off-by: Ngô Quốc Đạt <datlechin@gmail.com>
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.

Cannot open tables in custom schema in MSSQL

1 participant