Extending: the SDK seams
What a plugin can hook into beyond the four plugin types — the interfaces a provider or extension implements to teach SQL Explorer something new. Everything here is additive: a plugin that ignores a seam keeps working.
27, tools and extensions 4.Completion: functions and JOIN hints
The editor's completion is scope-aware on the host side — it understands CTEs, subqueries, derived tables and alias scopes, and does not leak across statement boundaries. A provider does not implement any of that. It only supplies the two things the host cannot know: your dialect's functions, and your foreign keys.
Function catalogue
ISqlDialect.Functions is a default-interface member returning an empty list, so declaring
one is optional. Each entry is a SqlFunction(Name, Signature, Doc?): Name is the
text that gets inserted, Signature is the detail line in the popup, Doc an
optional one-liner.
They are offered in expression positions — the SELECT list, WHERE,
ON, GROUP BY, ORDER BY, HAVING — and in the broad
fallback, but never right after FROM/JOIN or after alias..
Foreign keys, for JOIN … ON suggestions
The host offers a ready-made ON a.col = b.col when it can see the relationship. It reads that
from your schema tree, so the shape matters: expose a ForeignKeyFolder under the table with
ForeignKey nodes whose Detail uses the shared format — an arrow, not a
dash:
The host parses that back into structured foreign keys. An entry it does not recognise (a composite key
rendered some other way) is skipped rather than mis-suggested — so a malformed Detail
costs you the hint, not correctness. Column suggestions work the same way: Column nodes under
a ColumnFolder (or directly under a view), with the column type in Detail, which
becomes the tooltip.
Result paging: PageQuery
0.4.0 pages a single unbounded SELECT in the query editor (see
Query editor). The dialect decides how:
It is a default-interface member delegating to your existing Paginate. For
LIMIT/OFFSET dialects — PostgreSQL, MySQL, SQLite — that is already
correct, because LIMIT … OFFSET … may follow an optional ORDER BY.
No override needed.
ORDER BY, so the default would append a second one to a query that already has one, and the statement fails.The SQL Server dialect is the reference: when the query is already ordered, append to the existing
ORDER BY; otherwise fall back.
Container recipes
A provider can declare how to run its engine as an empty local container. The Local Containers
extension reads every declared recipe, so a third-party engine becomes provisionable without that
extension knowing anything about it. IDbProvider.ContainerRecipe defaults to
null — not containerisable, and the extension degrades gracefully.
Environment and the optional Command are delegates over a
ContainerEnvInput, so credentials and the database name are filled in at provisioning time.
The remaining knobs cover the engines that do not fit the default shape: NamedDatabase
(the engine has no create-database-on-start concept), DatabaseAfterStart,
Memlock, and HostPortOverride for a provider whose connection fields carry
their own port.
Extension capabilities: services and providers
A standing-subsystem extension declares capabilities and the user consents to them at install. Two were added in 0.4.0.
services — your own DI
Mark a class with ISingletonService, ITransientService or
IScopedService and the host registers it for you, then hands the plugin a resolver on
IPluginRuntimeContext.Services scoped to exactly those types. A plugin can never register
under — or resolve — a host contract; only its own. Without the capability the property is
null.
providers — read the container recipes
With this capability, IPluginRuntimeContext.Providers exposes an
IProviderCatalog: ContainerRecipes() returns one
ProviderRecipe(ProviderId, DisplayName, Recipe) per installed provider that declared one.
Read-only — the plugin learns which engines exist and how to provision them, and gains no control
over them. This is exactly how Local Containers discovers engines.
Panel icons and the shared icon set
A docked panel supplies its own toggle glyph through IPanelPlugin.Icon
(a Geometry?, default null). Draw it from SqlExplorer.Sdk.Ui.Icons,
the Lucide-derived geometries the host itself uses — one source of truth, so a plugin's icons stay in
step with the app instead of drifting from a copied SVG. Render with a themed stroke brush and
Stretch="Uniform".
Host-API versions
Every seam above is additive, so a plugin built against an older host keeps loading — it simply contributes nothing for the seams it predates. Declare the version you built against in your manifest; the loader gates on it.
SQL Explorer