A language server for the SQL files in Rust projects that use sqlx.
The server figures out which database your project targets by asking Cargo
which features are actually resolved for the sqlx dependency (so workspace
and transitive feature unification are handled correctly), builds a schema
index from your migrations, and serves editor features against that schema.
-
Database detection — reads the resolved features of the
sqlxpackage viacargo metadata.sqlite,postgres, andmysqlselect the matching SQL dialect for parsing; when several are enabled the server preferspostgres>mysql>sqliteand logs the ambiguity. -
Per-crate database contexts — the server mirrors how the sqlx macros resolve everything relative to the invoking crate. Each workspace member that depends on sqlx gets its own context: its
sqlx.toml(database-url-var,migrations-dir), its environment (the crate's URL variable from the process environment or ancestor.envfiles, parsed with dotenvy exactly like sqlx), its backend (the URL scheme decides, gated on the enabled driver features —postgres/postgresql,mysql/mariadb,sqlite— with declared features as the fallback), and its migrations (everysqlx::migrate!()target found in its sources, defaulting to the configured migrations directory). A workspace mixing a postgres crate and a sqlite crate serves each crate's SQL against the right schema and dialect. Documents outside any sqlx crate get a workspace-wide context that merges everything.SQLX_OFFLINE=truedisables live introspection for a context, matching the macros' contract. -
Schema index — replays the
.sqlmigrations a crate consumes in sqlx version order (skipping*.down.sql), applyingCREATE TABLE,CREATE VIEW,ALTER TABLE, andDROPstatements. Definitions keep their source locations. IfDATABASE_URL(from the environment or.env) points at a reachable database, the server also introspects it and fills in any relations the migrations don't cover. SQLite files are opened read-only; PostgreSQL is queried through its system catalogs on a session forced todefault_transaction_read_only, covering every table, view, and materialized view visible on the search path; MySQL is queried throughinformation_schemaon aREAD ONLYsession, covering the URL's database. Passwords never appear in logs or error messages. -
Completion — context-aware: tables after
FROM/JOIN/INTO/UPDATE, columns of the qualified relation afteralias.ortable., and in-scope columns plus tables, keywords, and common functions elsewhere. Works on syntactically incomplete statements. -
Hover — reconstructed
CREATE-statement summaries for tables and views, SQL signatures for columns, with the defining migration named. -
Goto definition — jumps from a table, alias, or column reference to the defining statement in the migration that created it.
-
Semantic tokens — full-document highlighting with a lexical base layer (keywords, literals, comments, operators, placeholders, type names) and an AST overlay that classifies tables, columns, aliases, and function names — including identifiers whose names collide with keywords.
-
Rust buffers — all of the above also work inside sqlx's query macros in Rust files. Tree-sitter locates the SQL string of
query!,query_as!,query_scalar!(and their_uncheckedvariants, bare orsqlx::-qualified), and the features run on the embedded SQL with results mapped back to Rust buffer coordinates. Semantic tokens cover only the SQL strings, layering cleanly on top of rust-analyzer's highlighting. Raw strings (r#"..."#) are handled losslessly; plain strings are read verbatim, without decoding escape sequences.query_file!is intentionally not handled here — the referenced.sqlfile is served directly.
The schema index reloads automatically when migrations, Cargo.toml, or
.env change (via client file watching, with a save-based fallback).
All three backends — SQLite, PostgreSQL, and MySQL — are fully supported, including live introspection.
Prebuilt binaries for Linux (gnu/musl), macOS, and Windows are attached to
the GitHub releases — the
rolling nightly prerelease tracks main. Or build from source:
cargo install --git https://github.com/willothy/sqlx-lspThe server speaks LSP over stdio. Point your editor's LSP client at the
sqlx-lsp binary for SQL files — and for Rust files too if you want
features inside the query macros; the server runs happily alongside
rust-analyzer and only answers for the embedded SQL.
Neovim (0.11+):
vim.lsp.config("sqlx_lsp", {
cmd = { "sqlx-lsp" },
filetypes = { "sql", "rust" },
root_markers = { "Cargo.toml" },
})
vim.lsp.enable("sqlx_lsp")VS Code: install sqlx-lsp.vsix from the
releases page with
code --install-extension sqlx-lsp.vsix. The extension lives in
editors/vscode and adds a TextMate injection grammar for
SQL coloring inside the query macros (VS Code takes semantic tokens from
only one provider per document, and rust-analyzer claims Rust files). Set
sqlx-lsp.serverPath if the binary is not on PATH.
Logging goes to stderr; set SQLX_LSP_LOG (a tracing filter, e.g. debug)
to adjust verbosity. Schema loading progress is also reported through
window/logMessage.
cargo metadata resolves the full dependency graph (scanning downward for
the manifest when the editor root is a plain monorepo root). Per crate, the
backend is chosen the way the sqlx macros select a driver: the crate's
database URL scheme decides, gated on the driver features its declared sqlx
dependency (or the workspace-unified feature set) enables; without a URL,
the highest-priority enabled driver wins (postgres > mysql > sqlite). If
detection fails entirely (not a Rust workspace, no sqlx dependency), the
server logs a warning and defaults to SQLite.
cargo test # tests
cargo clippy --all-targetsThe crate is a thin binary over a library (src/lib.rs); the interesting
modules are db (backend detection), schema (migration replay and the
schema index), introspect (read-only SQLite introspection), analysis/*
(the four language features), and embedded (tree-sitter extraction of SQL
from Rust query macros).