This document is normative for SQL editor intelligence in Studio (view=sql): syntax highlighting, schema-aware autocomplete, and async lint diagnostics. Natural-language SQL generation is governed separately by Architecture/sql-ai-generation.md.
The implementation MUST use the existing authenticated BFF transport and MUST NOT introduce an unauthenticated side channel.
This architecture governs:
- SQL language mode and syntax highlighting
- schema-aware completions (schemas/tables/columns)
- lint diagnostics lifecycle (request, cancellation, rendering)
- reuse of SQL lint transport for advanced table-level SQL filter pills
- backend lint safety guardrails and operational limits
- keyboard execution semantics for multi-statement SQL text
ui/studio/views/sql/SqlView.tsxui/studio/views/sql/sql-editor-config.tsui/studio/views/sql/sql-lint-source.tsdata/sql-editor-schema.tsdata/bff/bff-client.tsdata/postgres-core/adapter.tsdata/postgres-core/sql-lint.tsdata/mysql-core/adapter.tsdata/mysql-core/sql-lint.tsdata/sqlite-core/adapter.tsdata/sqlite-core/sql-lint.tsdemo/ppg-dev/sql-lint.tsdemo/ppg-dev/server.ts
- SQL editor MUST use CodeMirror 6.
- Dialect selection MUST come from adapter capability
sqlDialect:postgresql->PostgreSQLmysql->MySQLsqlite->SQLite
- Completions MUST be derived from introspection metadata (no ad-hoc hardcoded schema lists).
- Schema namespace normalization MUST be deterministic and versioned via
data/sql-editor-schema.ts. Mod+Enter(Cmd+Enteron macOS /Ctrl+Enteron Windows/Linux) MUST execute SQL.- For multi-statement editor text, execution MUST target the top-level statement containing the cursor (not the entire document).
- Lint source MUST be async and debounced (
delay: 500ms). - Lint requests MUST run with at most one active request per editor:
- starting a new request aborts the previous one
- stale responses MUST be discarded via request id check
- Diagnostics MUST be clamped to document bounds before returning to CodeMirror.
- Empty SQL MUST short-circuit with no lint request.
- Table-level SQL filter pills MAY reuse the same adapter
sqlLinttransport, but they MUST wrap rawWHEREfragments in a fullSELECT ... WHERE ...statement before linting. - SQL filter-pill linting MUST run asynchronously in the background and MUST NOT block the initial filter apply interaction.
- Table-level SQL filter-pill lint diagnostics are advisory UI state only and MUST NOT mutate the already-applied URL filter after the pill has been saved.
- Lint requests MUST flow through existing BFF request channel (
/api/query+procedure) using:- existing
customHeaders - existing
customPayload
- existing
- Procedure name for linting is
sql-lint. - New SQL-editor surfaces MUST NOT bypass BFF auth propagation.
- Executor contract:
ExecutorMAY exposelintSql(details, options)as an optimized lint transport.- Postgres adapters MUST use executor
lintSqlwhen present; otherwise they MUST fall back to adapter-levelEXPLAIN (FORMAT JSON)linting.
sql-lint execution MUST enforce all of the following:
- maximum SQL length (50KB)
- one or more top-level statements
- statement allowlist:
SELECT,WITH,VALUES,INSERT,UPDATE,DELETE
- parse/plan only execution (
EXPLAIN (FORMAT JSON) ...) - transaction-local guardrails:
statement_timeout = 1000mslock_timeout = 100msidle_in_transaction_session_timeout = 1000ms
On Postgres errors, diagnostics MUST include mapped position and SQLSTATE when available. Timeout diagnostics (57014) MUST be rewritten to a user-facing lint-timeout message.
For multi-statement SQL text, diagnostics MUST map statement-relative positions back to full-editor offsets.
sqlEditorAutocompleteis enabled for Postgres/MySQL/SQLite adapters.sqlEditorLintis enabled for Postgres/MySQL/SQLite adapters.- All adapters MUST follow this resolution order:
- use executor-provided
lintSqlwhen available - if
lintSqlis unavailable or returns unsupported-procedure/transport errors (invalid procedure,not supported,method not allowed, and equivalent 5xx transport failures such asunexpected server error), fall back to adapter-levelEXPLAINlinting through the normal executor path - when fallback is triggered by a persistent transport incompatibility, adapters SHOULD stop retrying
lintSqlfor the current adapter instance and continue withEXPLAINfallback
- use executor-provided
- Adapter fallback SQL:
- PostgreSQL:
EXPLAIN <statement> - MySQL:
EXPLAIN <statement> - SQLite:
EXPLAIN <statement>
- PostgreSQL:
- Lint traffic is high-frequency by nature; request cancellation and debounce are required, not optional.
- Lint queries MUST remain isolated from normal execution path semantics (never run with
EXPLAIN ANALYZE). - SQL view run/cancel flow remains independent from lint flow and must stay cancellable.
- MySQL and SQLite lint diagnostics are parser/planner best-effort and rely on engine error messages for token mapping (no position field equivalent to Postgres
Position).
Changes to SQL editor intelligence MUST include tests covering:
- schema namespace normalization and versioning
- BFF lint request serialization + auth payload propagation
- Postgres lint guardrail validation and error mapping
- MySQL and SQLite lint fallback diagnostics + multi-statement position mapping
- one-active-request lint cancellation behavior
- SQL view integration (run/cancel + read-only result grid) with CodeMirror present