a sql linter that lints like rust, ships like a binary.
drift parses postgres, mysql, sqlite, bigquery,
and ansi. 70 rules across 7 categories. about 50 to 200x faster than sqlfluff on the same corpus.
single 2.9mb binary, zero runtime deps.
$ cat reports.sql select * from users u join orders o on u.id=o.user_id where lower(u.email) like '%@oldcorp.com' order by u.id desc; update orders set status='cancelled' where amount<0; // 3 lines · 3 statements · waiting for drift
$ drift check reports.sql reports.sql:1:1 warning [drift.performance.select-star] reports.sql:1:22 warning [drift.style.keyword-case] reports.sql:2:2 info [drift.performance.fn-on-column] reports.sql:2:31 warning [drift.performance.like-leading-wildcard] reports.sql:3:1 error [drift.correctness.missing-where-guard] ──────────────────────────────────────────── 5 issues · 1 error · 4 warnings 270ms
$ drift fix reports.sql fixed reports.sql (3 safe rewrites applied, 2 left for you) SELECT * FROM users u JOIN orders o ON u.id=o.user_id WHERE LOWER(u.email) LIKE '%@oldcorp.com' ORDER BY u.id DESC; UPDATE orders SET status='cancelled' WHERE amount<0; // keyword-case · normalized // select-star · still yours. not safe to auto-rewrite // missing-where-guard · still yours. will not guess intent
$ drift rules | head -20 style 20 rules · keyword case, indent, comma style, alias quoting correctness 15 rules · missing where, cartesian join, null compare performance 8 rules · select *, like wildcards, fn on indexed column security 6 rules · plaintext passwords, grant all, superuser ddl portability 8 rules · dialect-specific syntax, non-standard casts conventions 8 rules · snake_case columns, singular table names ambiguity 5 rules · unqualified columns, duplicate aliases 70 rules total. each one has a doc page and a 'why'. run `drift explain <rule-id>` for the full writeup.
$ drift lsp & [lsp] listening on stdio [lsp] workspace root: ~/src/reports [lsp] loaded drift.toml (dialect = postgres) → helix · neovim · vscode · zed → real-time diagnostics on save + on type → quick-fixes wired through code actions → all 70 rules available in the editor // not a wrapper. speaks lsp 3.17 over stdio.
$ drift --version drift 0.16.0 (2.9mb, stripped, static) # dialects ready postgres 95% coverage · primary target mysql 80% coverage sqlite 80% coverage snowflake 70% coverage · LATERAL FLATTEN, QUALIFY, named args bigquery 60% coverage · window fns landing next ansi -- baseline fallback rust 1.75+ · mit · github.com/f4rkh4d/drift
drop a drift.toml next to your schema, run drift check, get violations
ranked by category. drift fix applies safe rewrites and refuses the unsafe ones.
drift lsp plugs into any editor that speaks lsp. 70 rules, 5 dialects, one binary.
real parser
built on sqlparser-rs with dialect-specific adapters per engine. drift reads an ast,
not a regex guess. means it can see scope, type, alias. means rules can actually reason.
six dialects
postgres is the primary and deepest. mysql, sqlite,
bigquery, snowflake behind it. ansi as fallback. drift auto-detects from
file extension or from [dialect] in the toml.
seventy-two rules
split across style, correctness, performance, security, portability, conventions, ambiguity.
each rule has its own markdown page in docs/rules/, generated from the rule trait
so they cannot drift from the code. drift explain rule-id prints the same content
inline.
fix that respects you
safe, deterministic rewrites only. drift will normalize keyword case all day. it will not
invent a where clause, because it does not know your intent. every fix is idempotent.
editor lsp, not a stub
real lsp 3.17 over stdio. diagnostics on save and on type, code actions for the quick-fixable rules. helix, neovim, vscode, zed picked it up with no glue. jetbrains is next, probably.
single 2.9mb binary
stripped, statically linked, zero runtime deps. cargo install drift-sql or grab
the install script. idles at ~4mb of ram. fits in your ci image without a second thought.
seven categories, one example each
the rule ids are stable. the fixes are not always automatic. drift tells you which is which.
style keyword-case // mixed SELECT/select in one file correctness missing-where-guard // unguarded update or delete performance like-leading-wildcard // '%foo' defeats the index security plaintext-password-column // column named 'password' as text portability non-standard-cast // ::text works in pg only conventions table-name-plural // prefer 'user' over 'users' (configurable) ambiguity unqualified-column // 'id' when two tables have one
conventions is opinionated on purpose and every rule in it ships off by default.
you opt in per repo. the rest are on with sensible severity. tweak anything in drift.toml.
drift vs the sql linters you already know
feature drift sqlfluff sqlfmt pgformatter ─────────────────────── ────── ──────── ──────── ─────────── multi-dialect 5 7+ dbt only postgres speed (10k stmts) ~270ms ~45s ~2s ~6s single binary yes no (pip) no (pip) yes (perl) editor lsp yes via ext no no fix mode yes yes format-only format-only config toml dotfiles toml cli flags memory (idle) ~4 MB ~120 MB ~80 MB ~1 MB
sqlfluff has the biggest rule catalog and the deepest jinja / dbt story. drift wins on speed, distribution, and editor ux. both tools are fine. pick the one that fits your stack.
install
curl -fsSL https://drift.frkhd.com/install.sh | sh
detects os + arch, drops the binary on your PATH. set DRIFT_INSTALL_DIR if you want a specific spot.
brew install f4rkh4d/tap/drift
macos + linuxbrew. formula pinned to the latest release. tap lands with the first tagged build.
cargo install drift-sql
the unscoped drift on crates.io is an unrelated openapi tool, so the package ships as drift-sql. the binary you get is still drift.
mac (apple silicon + intel) and linux (amd64 + arm64). binaries on the github releases page. windows is on the list but not here yet.
ci & integrations
- uses: f4rkh4d/drift@main
with:
paths: 'migrations/'
fail-on: error
composite action at the repo root. inputs: command, paths, fail-on, config, version, args.
drift check --format sarif migrations/ > drift.sarif
# upload via github/codeql-action/upload-sarif
SARIF 2.1.0. each violation surfaces as an inline annotation on the pull request that opened it.
repos:
- repo: https://github.com/f4rkh4d/drift
rev: v0.15.0
hooks: [{ id: drift-check }]
three hooks ship: drift-check, drift-fix, drift-format. install drift first via brew or the one-liner.
adopting drift on a legacy codebase
running drift cold against a 10-year-old SQL repo emits thousands of warnings, nobody fixes them, the team turns drift off. the baseline file fixes this.
# one-time: snapshot every current violation
drift baseline create migrations/ models/ analytics/
# from now on, only NEW violations surface. legacy debt is locked in.
drift check --baseline .drift-baseline.json migrations/ models/ analytics/
count-based per (file, rule) keying, so reformat / line-shift edits do not flip the suppression. the summary line keeps the debt visible: (N suppressed by baseline).
commit .drift-baseline.json to your repo. inspect with drift baseline show.