Skip to content

feat(plugins): list every schema's tables in one query on SQL Server and DuckDB - #3096

Merged
datlechin merged 2 commits into
mainfrom
feat/all-schema-table-listing-plugins
Sep 23, 2026
Merged

datlechin merged 2 commits into
mainfrom
feat/all-schema-table-listing-plugins

Conversation

@datlechin

Copy link
Copy Markdown
Member

What changed

SQL Server and DuckDB now answer fetchTablesInAllSchemas() (#3060) with one query. Open Quickly and the sidebar filter get every other schema's tables in one round trip instead of one per schema. Oracle and Snowflake stay on the per-schema fallback, for the measured and documented reasons under Left on the fallback.

One PR rather than one per engine: both engines follow the same pattern and share the Open Quickly docs table and the CHANGELOG, which a PR per engine would each have had to rebase over.

Query shape

This follows the PostgreSQL shape. The per-schema listing becomes a builder over a scope (.schema / .allSchemas). The all-schema form is the same statement with the schema predicate replaced by IN (<the query fetchSchemas() runs>), plus the schema as a projected column. So a table is listed exactly when its schema is one fetchSchemas() returns, with the same object kinds, exclusions and privilege semantics.

  • SQL Server: MSSQLSchemaQueries.tables(in:) in TableProMSSQLCore. SQL Server rejects ORDER BY in a subquery, so the schema list is split into listedSchemaNames (unordered) and schemas (that list plus ORDER BY SCHEMA_NAME). The plugin's fetchTables and fetchSchemas each inlined their own copy of these two queries. They now use the package's, so the listing, the schema list and the all-schema form come from one place, and parseTableRow reads the optional schema column. The iOS driver's calls (tables(schema:), schemas, parseTableRow) keep their signatures.
  • DuckDB: DuckDBSchemaQueries.listTables(in:). .allSchemas binds only the catalog ($1, reused inside the subquery) and filters by listSchemas. A remote Quack connection returns nil: its fetchSchemas() is best-effort and falls back to main, which one filtered query cannot reproduce.

System schemas work as they do on PostgreSQL. The host drops rows whose schema is in the engine's systemSchemaNames from a single-call answer, just as it drops those schemas before the per-schema fallback. SQL Server's schema list already leaves out sys, INFORMATION_SCHEMA, the db_* role schemas and guest, so those never reach the host. DuckDB's system catalogs are never the current catalog.

Parity evidence

  • scripts/check-mssql-table-listing-parity.sh builds a fixture with a view, an empty schema, mixed-case and dotted names, a schema where the reader holds a grant on one table only, and tables in the db_datareader and guest schemas the schema list excludes. It prints the real queries from TableProMSSQLCore, then diffs the per-schema union against the single query as sa and as a SELECT-only login, under the default collation and under Latin1_General_CS_AS. Azure SQL Edge 15.0.2000.1574 (ARM64): PASS, 7 objects as sa and 6 as the reader, in both collations. With the filter swapped for TABLE_SCHEMA IS NOT NULL it fails, because the role and guest schemas' tables show up.
  • scripts/check-duckdb-table-listing-parity.sh compiles the real DuckDBPluginDriver against Libs/libduckdb.a (v1.5.2). It compares fetchTables(schema:) over fetchSchemas() with fetchTablesInAllSchemas() in two attached catalogs: a sales schema in both, a temp table, and mixed-case and dotted names. PASS, with 6 and 2 objects. With the filter swapped for schema_name <> 'dot.ted' it fails.
  • scripts/check-duckdb-offline-metadata.sh now compiles DuckDBSchemaQueries.swift to print both listing forms. The listing is no longer a string constant its extractor can see. Both forms run with no extensions available.

Timing

Engine Schemas Per schema One query
SQL Server (Azure SQL Edge, local, one connection) 301, 2 tables each 4,701 to 7,051 ms 110 to 228 ms
DuckDB (real driver, in memory) 301, 2 tables each 2,685 to 5,249 ms 2 to 3 ms

Each range spans two runs on a machine running other builds.

Left on the fallback

  • Oracle: I implemented it the same way and it reached parity: 38 users, with 10,679 objects as SYSTEM and 2,022 as a SELECT-only user, on Oracle AI Database 26ai Free 23.26.3. I withdrew it because it is slower. The host drops Oracle's system schemas before the per-schema listing, so the fallback only asks the application users, 10 of 38 here, in 142 ms. One query has to read SYS and the other Oracle-maintained schemas for the host to discard: 9,169 ms as SYSTEM and 1,302 ms as the reader. To make it fast, the host's exclusion set has to reach the query, which needs a PluginKit parameter this PR does not add.
  • Snowflake: the per-schema listing is SHOW TERSE OBJECTS IN SCHEMA. The database-wide form is SHOW TERSE OBJECTS IN DATABASE, but a SHOW returns at most 10,000 records (SHOW OBJECTS usage notes). That cap applies per schema on one side and per database on the other, so a database with more than 10,000 objects would silently lose tables from the search. There is no server here to measure an INFORMATION_SCHEMA alternative, and switching the sidebar's listing to one untested is not this change.

Tests and verification

  • TableProMSSQLCoreTests (swift test): PASS, 36 cases in MSSQLSchemaQueriesTests. There are four new cases: the one-schema listing, the all-schema listing filtered by the unordered schema list, schemas as that list plus its ordering, and parseTableRow reading the schema column.
  • TableProTests: DuckDBSchemaQueriesTests, IndexStatementRenderingTests and CatalogTableListingTests PASS, 53 of 53 cases. DuckDBSchemaQueriesTests gains two cases, and its table-list case now runs over both scopes.
  • verify.sh build PASS, plugins (AllPlugins) PASS, lint clean on the six Swift files, docs PASS, and shellcheck --severity=warning clean.
  • No PluginKit change. TableProPluginKitVersion is 33 in the MSSQL and DuckDB Info.plists, equal to currentPluginKitVersion.
  • Two pre-existing lint lines in touched files were fixed: a space before a comma in DuckDBPlugin.swift, and a blank line before a closing brace in MSSQLPluginDriver+Schema.swift.

Review: a feature-dev:code-reviewer agent, since Codex is unavailable until Sep 29. It found no correctness bugs. Its one finding, that Oracle had no parity script, no longer applies now that Oracle stays on the fallback.

Follow-up to #3060 (#3048).

@mintlify

mintlify Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

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

Project Status Preview Updated
TablePro 🟢 Ready View Preview Sep 23, 2026, 7:22 PM

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

…-listing-plugins

# Conflicts:
#	CHANGELOG.md
@datlechin
datlechin merged commit 45a8c1f into main Sep 23, 2026
7 of 8 checks passed
@datlechin
datlechin deleted the feat/all-schema-table-listing-plugins branch September 23, 2026 19:24

This branch was successfully deployed

1 active deployment
staging - docs — 8190cbe6 Deployed Sep 23, 2026 by mintlify[bot]
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